Support importing modules in cinterop
Modules can be imported by using 'modules =' in .def file
This commit is contained in:
committed by
SvyatoslavScherbina
parent
67b06b5f2a
commit
1f60745f4b
+3
-3
@@ -16,14 +16,14 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.native.interop.indexer
|
package org.jetbrains.kotlin.native.interop.indexer
|
||||||
|
|
||||||
import java.nio.file.Paths
|
import java.io.File
|
||||||
|
|
||||||
class HeaderToIdMapper(sysRoot: String) {
|
class HeaderToIdMapper(sysRoot: String) {
|
||||||
private val headerPathToId = mutableMapOf<String, HeaderId>()
|
private val headerPathToId = mutableMapOf<String, HeaderId>()
|
||||||
private val sysRoot = Paths.get(sysRoot).normalize()
|
private val sysRoot = File(sysRoot).canonicalFile.toPath()
|
||||||
|
|
||||||
internal fun getHeaderId(filePath: String) = headerPathToId.getOrPut(filePath) {
|
internal fun getHeaderId(filePath: String) = headerPathToId.getOrPut(filePath) {
|
||||||
val path = Paths.get(filePath)
|
val path = File(filePath).canonicalFile.toPath()
|
||||||
val headerIdValue = if (path.startsWith(sysRoot)) {
|
val headerIdValue = if (path.startsWith(sysRoot)) {
|
||||||
val relative = sysRoot.relativize(path)
|
val relative = sysRoot.relativize(path)
|
||||||
relative.toString()
|
relative.toString()
|
||||||
|
|||||||
+2
-5
@@ -99,7 +99,7 @@ internal class NativeIndexImpl(val library: NativeLibrary, val verbose: Boolean
|
|||||||
all[key] = value
|
all[key] = value
|
||||||
|
|
||||||
val headerId = getHeaderId(getContainingFile(cursor))
|
val headerId = getHeaderId(getContainingFile(cursor))
|
||||||
if (!library.headerInclusionPolicy.excludeAll(headerId)) {
|
if (!library.headerExclusionPolicy.excludeAll(headerId)) {
|
||||||
// This declaration is used, and thus should be included:
|
// This declaration is used, and thus should be included:
|
||||||
included.add(value)
|
included.add(value)
|
||||||
}
|
}
|
||||||
@@ -917,8 +917,7 @@ fun buildNativeIndexImpl(library: NativeLibrary, verbose: Boolean): NativeIndex
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun indexDeclarations(nativeIndex: NativeIndexImpl) {
|
private fun indexDeclarations(nativeIndex: NativeIndexImpl) {
|
||||||
val index = clang_createIndex(0, 0)!!
|
withIndex { index ->
|
||||||
try {
|
|
||||||
val translationUnit = nativeIndex.library.parse(index, options = CXTranslationUnit_DetailedPreprocessingRecord)
|
val translationUnit = nativeIndex.library.parse(index, options = CXTranslationUnit_DetailedPreprocessingRecord)
|
||||||
try {
|
try {
|
||||||
translationUnit.ensureNoCompileErrors()
|
translationUnit.ensureNoCompileErrors()
|
||||||
@@ -959,7 +958,5 @@ private fun indexDeclarations(nativeIndex: NativeIndexImpl) {
|
|||||||
} finally {
|
} finally {
|
||||||
clang_disposeTranslationUnit(translationUnit)
|
clang_disposeTranslationUnit(translationUnit)
|
||||||
}
|
}
|
||||||
} finally {
|
|
||||||
clang_disposeIndex(index)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-5
@@ -50,8 +50,7 @@ private fun expandMacros(
|
|||||||
// so precompile library headers to significantly speed up the parsing:
|
// so precompile library headers to significantly speed up the parsing:
|
||||||
val library = originalLibrary.precompileHeaders()
|
val library = originalLibrary.precompileHeaders()
|
||||||
|
|
||||||
val index = clang_createIndex(excludeDeclarationsFromPCH = 1, displayDiagnostics = 0)!!
|
withIndex(excludeDeclarationsFromPCH = true) { index ->
|
||||||
try {
|
|
||||||
val sourceFile = library.createTempSource()
|
val sourceFile = library.createTempSource()
|
||||||
// We disable implicit function declaration to filter out cases when a macro is expanded as a function
|
// We disable implicit function declaration to filter out cases when a macro is expanded as a function
|
||||||
// or function-like construction (e.g. #define FOO throw()) but such a function is undeclared.
|
// or function-like construction (e.g. #define FOO throw()) but such a function is undeclared.
|
||||||
@@ -65,9 +64,6 @@ private fun expandMacros(
|
|||||||
} finally {
|
} finally {
|
||||||
clang_disposeTranslationUnit(translationUnit)
|
clang_disposeTranslationUnit(translationUnit)
|
||||||
}
|
}
|
||||||
|
|
||||||
} finally {
|
|
||||||
clang_disposeIndex(index)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+114
@@ -0,0 +1,114 @@
|
|||||||
|
package org.jetbrains.kotlin.native.interop.indexer
|
||||||
|
|
||||||
|
import clang.*
|
||||||
|
import kotlinx.cinterop.*
|
||||||
|
|
||||||
|
data class ModulesInfo(val topLevelHeaders: List<String>, val ownHeaders: Set<String>)
|
||||||
|
|
||||||
|
fun getModulesInfo(compilation: Compilation, modules: List<String>): ModulesInfo {
|
||||||
|
if (modules.isEmpty()) return ModulesInfo(emptyList(), emptySet())
|
||||||
|
|
||||||
|
withIndex { index ->
|
||||||
|
|
||||||
|
val ownHeaders = mutableSetOf<String>()
|
||||||
|
val topLevelHeaders = linkedSetOf<String>()
|
||||||
|
|
||||||
|
getModulesASTFiles(index, compilation, modules).forEach {
|
||||||
|
val moduleTranslationUnit = clang_createTranslationUnit(index, it)!!
|
||||||
|
try {
|
||||||
|
val modulesHeaders = getModulesHeaders(index, moduleTranslationUnit, modules.toSet(), topLevelHeaders)
|
||||||
|
modulesHeaders.mapTo(ownHeaders) { it.canonicalPath }
|
||||||
|
} finally {
|
||||||
|
clang_disposeTranslationUnit(moduleTranslationUnit)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ModulesInfo(topLevelHeaders.toList(), ownHeaders)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getModulesASTFiles(index: CXIndex, compilation: Compilation, modules: List<String>): List<String> {
|
||||||
|
val compilationWithImports = object : Compilation by compilation {
|
||||||
|
override val compilerArgs = compilation.compilerArgs + "-fmodules"
|
||||||
|
override val additionalPreambleLines = modules.map { "@import $it;" } + compilation.additionalPreambleLines
|
||||||
|
}
|
||||||
|
|
||||||
|
val result = linkedSetOf<String>()
|
||||||
|
|
||||||
|
val translationUnit = compilationWithImports.parse(
|
||||||
|
index,
|
||||||
|
options = CXTranslationUnit_DetailedPreprocessingRecord
|
||||||
|
)
|
||||||
|
try {
|
||||||
|
translationUnit.ensureNoCompileErrors()
|
||||||
|
|
||||||
|
indexTranslationUnit(index, translationUnit, 0, object : Indexer {
|
||||||
|
override fun importedASTFile(info: CXIdxImportedASTFileInfo) {
|
||||||
|
result += info.file!!.canonicalPath
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} finally {
|
||||||
|
clang_disposeTranslationUnit(translationUnit)
|
||||||
|
}
|
||||||
|
return result.toList()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getModulesHeaders(
|
||||||
|
index: CXIndex,
|
||||||
|
translationUnit: CXTranslationUnit,
|
||||||
|
modules: Set<String>,
|
||||||
|
topLevelHeaders: LinkedHashSet<String>
|
||||||
|
): Set<CXFile> {
|
||||||
|
val nonModularIncludes = mutableMapOf<CXFile, MutableSet<CXFile>>()
|
||||||
|
val result = mutableSetOf<CXFile>()
|
||||||
|
|
||||||
|
indexTranslationUnit(index, translationUnit, 0, object : Indexer {
|
||||||
|
override fun ppIncludedFile(info: CXIdxIncludedFileInfo) {
|
||||||
|
val file = info.file!!
|
||||||
|
val includer = clang_indexLoc_getCXSourceLocation(info.hashLoc.readValue()).getContainingFile()
|
||||||
|
|
||||||
|
if (includer == null) {
|
||||||
|
// i.e. the header is included by the module itself.
|
||||||
|
topLevelHeaders += file.path
|
||||||
|
}
|
||||||
|
|
||||||
|
val module = clang_getModuleForFile(translationUnit, file)
|
||||||
|
|
||||||
|
if (module != null) {
|
||||||
|
val moduleWithParents = generateSequence(module, { clang_Module_getParent(it) }).map {
|
||||||
|
clang_Module_getFullName(it).convertAndDispose()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (moduleWithParents.any { it in modules }) {
|
||||||
|
result += file
|
||||||
|
}
|
||||||
|
} else if (includer != null) {
|
||||||
|
nonModularIncludes.getOrPut(includer, { mutableSetOf() }) += file
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
// There are cases when non-modular includes should also be considered as a part of module. For example:
|
||||||
|
// 1. Some module maps are broken,
|
||||||
|
// e.g. system header `IOKit/hid/IOHIDProperties.h` isn't included to framework module map at all.
|
||||||
|
// 2. Textual headers are reported as non-modular by libclang.
|
||||||
|
//
|
||||||
|
// Find and include non-modular headers too:
|
||||||
|
result += findReachable(roots = result, arcs = nonModularIncludes)
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun <T> findReachable(roots: Set<T>, arcs: Map<T, Set<T>>): Set<T> {
|
||||||
|
val visited = mutableSetOf<T>()
|
||||||
|
|
||||||
|
fun dfs(vertex: T) {
|
||||||
|
if (!visited.add(vertex)) return
|
||||||
|
arcs[vertex].orEmpty().forEach { dfs(it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
roots.forEach { dfs(it) }
|
||||||
|
|
||||||
|
return visited
|
||||||
|
}
|
||||||
+26
-8
@@ -29,7 +29,9 @@ interface HeaderInclusionPolicy {
|
|||||||
* or `null` for builtin declarations.
|
* or `null` for builtin declarations.
|
||||||
*/
|
*/
|
||||||
fun excludeUnused(headerName: String?): Boolean
|
fun excludeUnused(headerName: String?): Boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
interface HeaderExclusionPolicy {
|
||||||
/**
|
/**
|
||||||
* Whether all declarations from this header should be excluded.
|
* Whether all declarations from this header should be excluded.
|
||||||
*
|
*
|
||||||
@@ -37,18 +39,34 @@ interface HeaderInclusionPolicy {
|
|||||||
* but not included into the root collections.
|
* but not included into the root collections.
|
||||||
*/
|
*/
|
||||||
fun excludeAll(headerId: HeaderId): Boolean
|
fun excludeAll(headerId: HeaderId): Boolean
|
||||||
|
|
||||||
// TODO: these methods should probably be combined into the only one, but it would require some refactoring.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
data class NativeLibrary(val includes: List<String>,
|
sealed class NativeLibraryHeaderFilter {
|
||||||
val additionalPreambleLines: List<String>,
|
class NameBased(
|
||||||
val compilerArgs: List<String>,
|
val policy: HeaderInclusionPolicy,
|
||||||
|
val excludeDepdendentModules: Boolean
|
||||||
|
) : NativeLibraryHeaderFilter()
|
||||||
|
|
||||||
|
class Predefined(val headers: Set<String>) : NativeLibraryHeaderFilter()
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Compilation {
|
||||||
|
val includes: List<String>
|
||||||
|
val additionalPreambleLines: List<String>
|
||||||
|
val compilerArgs: List<String>
|
||||||
|
val language: Language
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: consider replacing inheritance by composition and turning Compilation into final class.
|
||||||
|
|
||||||
|
data class NativeLibrary(override val includes: List<String>,
|
||||||
|
override val additionalPreambleLines: List<String>,
|
||||||
|
override val compilerArgs: List<String>,
|
||||||
val headerToIdMapper: HeaderToIdMapper,
|
val headerToIdMapper: HeaderToIdMapper,
|
||||||
val language: Language,
|
override val language: Language,
|
||||||
val excludeSystemLibs: Boolean, // TODO: drop?
|
val excludeSystemLibs: Boolean, // TODO: drop?
|
||||||
val excludeDepdendentModules: Boolean,
|
val headerExclusionPolicy: HeaderExclusionPolicy,
|
||||||
val headerInclusionPolicy: HeaderInclusionPolicy)
|
val headerFilter: NativeLibraryHeaderFilter) : Compilation
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves the definitions from given C header file using given compiler arguments (e.g. defines).
|
* Retrieves the definitions from given C header file using given compiler arguments (e.g. defines).
|
||||||
|
|||||||
+94
-34
@@ -87,6 +87,23 @@ internal fun convertUnqualifiedPrimitiveType(type: CValue<CXType>): Type = when
|
|||||||
else -> UnsupportedType
|
else -> UnsupportedType
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal inline fun <R> withIndex(
|
||||||
|
excludeDeclarationsFromPCH: Boolean = false,
|
||||||
|
displayDiagnostics: Boolean = false,
|
||||||
|
block: (index: CXIndex) -> R
|
||||||
|
): R {
|
||||||
|
val index = clang_createIndex(
|
||||||
|
excludeDeclarationsFromPCH = if (excludeDeclarationsFromPCH) 1 else 0,
|
||||||
|
displayDiagnostics = if (displayDiagnostics) 1 else 0
|
||||||
|
)!!
|
||||||
|
|
||||||
|
return try {
|
||||||
|
block(index)
|
||||||
|
} finally {
|
||||||
|
clang_disposeIndex(index)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
internal fun parseTranslationUnit(
|
internal fun parseTranslationUnit(
|
||||||
index: CXIndex,
|
index: CXIndex,
|
||||||
sourceFile: File,
|
sourceFile: File,
|
||||||
@@ -107,7 +124,7 @@ internal fun parseTranslationUnit(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun NativeLibrary.parse(index: CXIndex, options: Int = 0): CXTranslationUnit =
|
internal fun Compilation.parse(index: CXIndex, options: Int = 0): CXTranslationUnit =
|
||||||
parseTranslationUnit(index, this.createTempSource(), this.compilerArgs, options)
|
parseTranslationUnit(index, this.createTempSource(), this.compilerArgs, options)
|
||||||
|
|
||||||
internal data class Diagnostic(val severity: CXDiagnosticSeverity, val format: String,
|
internal data class Diagnostic(val severity: CXDiagnosticSeverity, val format: String,
|
||||||
@@ -218,11 +235,11 @@ internal fun List<String>.toNativeStringArray(scope: AutofreeScope): CArrayPoint
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val NativeLibrary.preambleLines: List<String>
|
val Compilation.preambleLines: List<String>
|
||||||
get() = this.includes.map { "#include <$it>" } + this.additionalPreambleLines
|
get() = this.includes.map { "#include <$it>" } + this.additionalPreambleLines
|
||||||
|
|
||||||
internal fun Appendable.appendPreamble(library: NativeLibrary) = this.apply {
|
internal fun Appendable.appendPreamble(compilation: Compilation) = this.apply {
|
||||||
library.preambleLines.forEach {
|
compilation.preambleLines.forEach {
|
||||||
this.appendln(it)
|
this.appendln(it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -230,7 +247,7 @@ internal fun Appendable.appendPreamble(library: NativeLibrary) = this.apply {
|
|||||||
/**
|
/**
|
||||||
* Creates temporary source file which includes the library.
|
* Creates temporary source file which includes the library.
|
||||||
*/
|
*/
|
||||||
internal fun NativeLibrary.createTempSource(): File {
|
internal fun Compilation.createTempSource(): File {
|
||||||
val result = createTempFile(suffix = ".${language.sourceFileExtension}")
|
val result = createTempFile(suffix = ".${language.sourceFileExtension}")
|
||||||
result.deleteOnExit()
|
result.deleteOnExit()
|
||||||
|
|
||||||
@@ -249,8 +266,7 @@ internal fun NativeLibrary.createTempSource(): File {
|
|||||||
internal fun NativeLibrary.precompileHeaders(): NativeLibrary {
|
internal fun NativeLibrary.precompileHeaders(): NativeLibrary {
|
||||||
val precompiledHeader = createTempFile(suffix = ".pch").apply { this.deleteOnExit() }
|
val precompiledHeader = createTempFile(suffix = ".pch").apply { this.deleteOnExit() }
|
||||||
|
|
||||||
val index = clang_createIndex(excludeDeclarationsFromPCH = 0, displayDiagnostics = 0)!!
|
withIndex { index ->
|
||||||
try {
|
|
||||||
val options = CXTranslationUnit_ForSerialization
|
val options = CXTranslationUnit_ForSerialization
|
||||||
val translationUnit = this.parse(index, options)
|
val translationUnit = this.parse(index, options)
|
||||||
try {
|
try {
|
||||||
@@ -259,8 +275,6 @@ internal fun NativeLibrary.precompileHeaders(): NativeLibrary {
|
|||||||
} finally {
|
} finally {
|
||||||
clang_disposeTranslationUnit(translationUnit)
|
clang_disposeTranslationUnit(translationUnit)
|
||||||
}
|
}
|
||||||
} finally {
|
|
||||||
clang_disposeIndex(index)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.copy(
|
return this.copy(
|
||||||
@@ -301,8 +315,7 @@ fun List<List<String>>.mapFragmentIsCompilable(originalLibrary: NativeLibrary):
|
|||||||
|
|
||||||
val fragmentsToCheck = this.withIndex().toMutableList()
|
val fragmentsToCheck = this.withIndex().toMutableList()
|
||||||
|
|
||||||
val index = clang_createIndex(excludeDeclarationsFromPCH = 1, displayDiagnostics = 0)!!
|
withIndex(excludeDeclarationsFromPCH = true) { index ->
|
||||||
try {
|
|
||||||
val sourceFile = library.createTempSource()
|
val sourceFile = library.createTempSource()
|
||||||
val translationUnit = parseTranslationUnit(index, sourceFile, library.compilerArgs, options = 0)
|
val translationUnit = parseTranslationUnit(index, sourceFile, library.compilerArgs, options = 0)
|
||||||
try {
|
try {
|
||||||
@@ -342,8 +355,6 @@ fun List<List<String>>.mapFragmentIsCompilable(originalLibrary: NativeLibrary):
|
|||||||
} finally {
|
} finally {
|
||||||
clang_disposeTranslationUnit(translationUnit)
|
clang_disposeTranslationUnit(translationUnit)
|
||||||
}
|
}
|
||||||
} finally {
|
|
||||||
clang_disposeIndex(index)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.indices.map { it !in indicesOfNonCompilable }
|
return this.indices.map { it !in indicesOfNonCompilable }
|
||||||
@@ -360,6 +371,11 @@ internal interface Indexer {
|
|||||||
*/
|
*/
|
||||||
fun ppIncludedFile(info: CXIdxIncludedFileInfo) {}
|
fun ppIncludedFile(info: CXIdxIncludedFileInfo) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when a AST file (PCH or module) gets imported.
|
||||||
|
*/
|
||||||
|
fun importedASTFile(info: CXIdxImportedASTFileInfo) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called to index a declaration.
|
* Called to index a declaration.
|
||||||
*/
|
*/
|
||||||
@@ -390,7 +406,14 @@ internal fun indexTranslationUnit(index: CXIndex, translationUnit: CXTranslation
|
|||||||
@Suppress("USELESS_CAST")
|
@Suppress("USELESS_CAST")
|
||||||
null as CXIdxClientFile?
|
null as CXIdxClientFile?
|
||||||
}
|
}
|
||||||
importedASTFile = null
|
importedASTFile = staticCFunction { clientData, info ->
|
||||||
|
@Suppress("NAME_SHADOWING")
|
||||||
|
val indexer = clientData!!.asStableRef<Indexer>().get()
|
||||||
|
indexer.importedASTFile(info!!.pointed)
|
||||||
|
// We must ensure only interop types exist in function signature.
|
||||||
|
@Suppress("USELESS_CAST")
|
||||||
|
null as CXIdxClientFile?
|
||||||
|
}
|
||||||
startedTranslationUnit = null
|
startedTranslationUnit = null
|
||||||
indexDeclaration = staticCFunction { clientData, info ->
|
indexDeclaration = staticCFunction { clientData, info ->
|
||||||
@Suppress("NAME_SHADOWING")
|
@Suppress("NAME_SHADOWING")
|
||||||
@@ -418,7 +441,7 @@ internal fun indexTranslationUnit(index: CXIndex, translationUnit: CXTranslation
|
|||||||
}
|
}
|
||||||
|
|
||||||
internal class ModulesMap(
|
internal class ModulesMap(
|
||||||
val library: NativeLibrary,
|
val compilation: Compilation,
|
||||||
val translationUnit: CXTranslationUnit
|
val translationUnit: CXTranslationUnit
|
||||||
) : Closeable {
|
) : Closeable {
|
||||||
|
|
||||||
@@ -428,10 +451,9 @@ internal class ModulesMap(
|
|||||||
init {
|
init {
|
||||||
index = clang_createIndex(0, 0)!!
|
index = clang_createIndex(0, 0)!!
|
||||||
try {
|
try {
|
||||||
translationUnitWithModules =
|
translationUnitWithModules = object : Compilation by compilation {
|
||||||
library
|
override val compilerArgs = compilation.compilerArgs + "-fmodules"
|
||||||
.copy(compilerArgs = library.compilerArgs + "-fmodules")
|
}.parse(index)
|
||||||
.parse(index)
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
translationUnitWithModules.ensureNoCompileErrors()
|
translationUnitWithModules.ensureNoCompileErrors()
|
||||||
@@ -466,15 +488,12 @@ internal class ModulesMap(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun HeaderInclusionPolicy.includeAll(headerName: String?, headerId: HeaderId): Boolean =
|
|
||||||
!this.excludeUnused(headerName) && !this.excludeAll(headerId)
|
|
||||||
|
|
||||||
internal fun getHeaderId(library: NativeLibrary, header: CXFile?): HeaderId {
|
internal fun getHeaderId(library: NativeLibrary, header: CXFile?): HeaderId {
|
||||||
if (header == null) {
|
if (header == null) {
|
||||||
return HeaderId("builtins")
|
return HeaderId("builtins")
|
||||||
}
|
}
|
||||||
|
|
||||||
val filePath = clang_getFileName(header).convertAndDispose()
|
val filePath = header.path
|
||||||
return library.headerToIdMapper.getHeaderId(filePath)
|
return library.headerToIdMapper.getHeaderId(filePath)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -493,6 +512,30 @@ internal fun getHeaders(
|
|||||||
): NativeLibraryHeaders<CXFile?> {
|
): NativeLibraryHeaders<CXFile?> {
|
||||||
val ownHeaders = mutableSetOf<CXFile?>()
|
val ownHeaders = mutableSetOf<CXFile?>()
|
||||||
val allHeaders = mutableSetOf<CXFile?>(null)
|
val allHeaders = mutableSetOf<CXFile?>(null)
|
||||||
|
|
||||||
|
val filter = library.headerFilter
|
||||||
|
|
||||||
|
when (filter) {
|
||||||
|
is NativeLibraryHeaderFilter.NameBased ->
|
||||||
|
filterHeadersByName(library, filter, index, translationUnit, ownHeaders, allHeaders)
|
||||||
|
|
||||||
|
is NativeLibraryHeaderFilter.Predefined ->
|
||||||
|
filterHeadersByPredefined(filter, index, translationUnit, ownHeaders, allHeaders)
|
||||||
|
}
|
||||||
|
|
||||||
|
ownHeaders.removeAll { library.headerExclusionPolicy.excludeAll(getHeaderId(library, it)) }
|
||||||
|
|
||||||
|
return NativeLibraryHeaders(ownHeaders, allHeaders - ownHeaders)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun filterHeadersByName(
|
||||||
|
compilation: Compilation,
|
||||||
|
filter: NativeLibraryHeaderFilter.NameBased,
|
||||||
|
index: CXIndex,
|
||||||
|
translationUnit: CXTranslationUnit,
|
||||||
|
ownHeaders: MutableSet<CXFile?>,
|
||||||
|
allHeaders: MutableSet<CXFile?>
|
||||||
|
) {
|
||||||
val topLevelFiles = mutableListOf<CXFile>()
|
val topLevelFiles = mutableListOf<CXFile>()
|
||||||
var mainFile: CXFile? = null
|
var mainFile: CXFile? = null
|
||||||
|
|
||||||
@@ -524,7 +567,7 @@ internal fun getHeaders(
|
|||||||
// If it is included with `#include "$name"`, then `name` can also be the path relative to the includer.
|
// If it is included with `#include "$name"`, then `name` can also be the path relative to the includer.
|
||||||
val includerFile = includeLocation.getContainingFile()!!
|
val includerFile = includeLocation.getContainingFile()!!
|
||||||
val includerName = headerToName[includerFile] ?: ""
|
val includerName = headerToName[includerFile] ?: ""
|
||||||
val includerPath = clang_getFileName(includerFile).convertAndDispose()
|
val includerPath = includerFile.path
|
||||||
|
|
||||||
if (clang_getFile(translationUnit, Paths.get(includerPath).resolveSibling(name).toString()) == file) {
|
if (clang_getFile(translationUnit, Paths.get(includerPath).resolveSibling(name).toString()) == file) {
|
||||||
// included file is accessible from the includer by `name` used as relative path, so
|
// included file is accessible from the includer by `name` used as relative path, so
|
||||||
@@ -536,14 +579,14 @@ internal fun getHeaders(
|
|||||||
}
|
}
|
||||||
|
|
||||||
headerToName[file] = headerName
|
headerToName[file] = headerName
|
||||||
if (library.headerInclusionPolicy.includeAll(headerName, getHeaderId(library, file))) {
|
if (!filter.policy.excludeUnused(headerName)) {
|
||||||
ownHeaders.add(file)
|
ownHeaders.add(file)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
if (library.excludeDepdendentModules) {
|
if (filter.excludeDepdendentModules) {
|
||||||
ModulesMap(library, translationUnit).use { modulesMap ->
|
ModulesMap(compilation, translationUnit).use { modulesMap ->
|
||||||
val topLevelModules = topLevelFiles.map { modulesMap.getModule(it) }.toSet()
|
val topLevelModules = topLevelFiles.map { modulesMap.getModule(it) }.toSet()
|
||||||
ownHeaders.removeAll {
|
ownHeaders.removeAll {
|
||||||
val module = modulesMap.getModule(it!!)
|
val module = modulesMap.getModule(it!!)
|
||||||
@@ -553,25 +596,41 @@ internal fun getHeaders(
|
|||||||
// then all non-modular headers are included.
|
// then all non-modular headers are included.
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (library.headerInclusionPolicy.includeAll(headerName = null, headerId = getHeaderId(library, null))) {
|
if (!filter.policy.excludeUnused(headerName = null)) {
|
||||||
// Builtins.
|
// Builtins.
|
||||||
ownHeaders.add(null)
|
ownHeaders.add(null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ownHeaders.add(mainFile!!)
|
ownHeaders.add(mainFile!!)
|
||||||
|
}
|
||||||
|
|
||||||
return NativeLibraryHeaders(ownHeaders, allHeaders - ownHeaders)
|
private fun filterHeadersByPredefined(
|
||||||
|
filter: NativeLibraryHeaderFilter.Predefined,
|
||||||
|
index: CXIndex,
|
||||||
|
translationUnit: CXTranslationUnit,
|
||||||
|
ownHeaders: MutableSet<CXFile?>,
|
||||||
|
allHeaders: MutableSet<CXFile?>
|
||||||
|
) {
|
||||||
|
// Note: suboptimal but simple.
|
||||||
|
indexTranslationUnit(index, translationUnit, 0, object : Indexer {
|
||||||
|
override fun ppIncludedFile(info: CXIdxIncludedFileInfo) {
|
||||||
|
val file = info.file
|
||||||
|
allHeaders += file
|
||||||
|
if (file?.canonicalPath in filter.headers) {
|
||||||
|
ownHeaders += file
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fun NativeLibrary.getHeaderPaths(): NativeLibraryHeaders<String> {
|
fun NativeLibrary.getHeaderPaths(): NativeLibraryHeaders<String> {
|
||||||
val index = clang_createIndex(0, 0)!!
|
withIndex { index ->
|
||||||
try {
|
|
||||||
val translationUnit =
|
val translationUnit =
|
||||||
this.parse(index, options = CXTranslationUnit_DetailedPreprocessingRecord).ensureNoCompileErrors()
|
this.parse(index, options = CXTranslationUnit_DetailedPreprocessingRecord).ensureNoCompileErrors()
|
||||||
try {
|
try {
|
||||||
|
|
||||||
fun getPath(file: CXFile?) = if (file == null) "<builtins>" else clang_getFileName(file).convertAndDispose()
|
fun getPath(file: CXFile?) = if (file == null) "<builtins>" else file.canonicalPath
|
||||||
|
|
||||||
val headers = getHeaders(this, index, translationUnit)
|
val headers = getHeaders(this, index, translationUnit)
|
||||||
return NativeLibraryHeaders(
|
return NativeLibraryHeaders(
|
||||||
@@ -581,8 +640,6 @@ fun NativeLibrary.getHeaderPaths(): NativeLibraryHeaders<String> {
|
|||||||
} finally {
|
} finally {
|
||||||
clang_disposeTranslationUnit(translationUnit)
|
clang_disposeTranslationUnit(translationUnit)
|
||||||
}
|
}
|
||||||
} finally {
|
|
||||||
clang_disposeIndex(index)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -618,6 +675,9 @@ internal fun getContainingFile(cursor: CValue<CXCursor>): CXFile? {
|
|||||||
return clang_getCursorLocation(cursor).getContainingFile()
|
return clang_getCursorLocation(cursor).getContainingFile()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal val CXFile.path: String get() = clang_getFileName(this).convertAndDispose()
|
||||||
|
internal val CXFile.canonicalPath: String get() = File(this.path).canonicalPath
|
||||||
|
|
||||||
private fun createVfsOverlayFileContents(virtualPathToReal: Map<Path, Path>): ByteArray {
|
private fun createVfsOverlayFileContents(virtualPathToReal: Map<Path, Path>): ByteArray {
|
||||||
val overlay = clang_VirtualFileOverlay_create(0)
|
val overlay = clang_VirtualFileOverlay_create(0)
|
||||||
|
|
||||||
|
|||||||
+7
-7
@@ -16,9 +16,7 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.native.interop.gen
|
package org.jetbrains.kotlin.native.interop.gen
|
||||||
|
|
||||||
import org.jetbrains.kotlin.native.interop.indexer.HeaderId
|
import org.jetbrains.kotlin.native.interop.indexer.*
|
||||||
import org.jetbrains.kotlin.native.interop.indexer.HeaderInclusionPolicy
|
|
||||||
import org.jetbrains.kotlin.native.interop.indexer.Location
|
|
||||||
|
|
||||||
interface Imports {
|
interface Imports {
|
||||||
fun getPackage(location: Location): String?
|
fun getPackage(location: Location): String?
|
||||||
@@ -31,10 +29,7 @@ class ImportsImpl(internal val headerIdToPackage: Map<HeaderId, String>) : Impor
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class HeadersInclusionPolicyImpl(
|
class HeaderInclusionPolicyImpl(private val nameGlobs: List<String>) : HeaderInclusionPolicy {
|
||||||
private val nameGlobs: List<String>,
|
|
||||||
private val importsImpl: ImportsImpl
|
|
||||||
) : HeaderInclusionPolicy {
|
|
||||||
|
|
||||||
override fun excludeUnused(headerName: String?): Boolean {
|
override fun excludeUnused(headerName: String?): Boolean {
|
||||||
if (nameGlobs.isEmpty()) {
|
if (nameGlobs.isEmpty()) {
|
||||||
@@ -48,6 +43,11 @@ class HeadersInclusionPolicyImpl(
|
|||||||
|
|
||||||
return nameGlobs.all { !headerName.matchesToGlob(it) }
|
return nameGlobs.all { !headerName.matchesToGlob(it) }
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class HeaderExclusionPolicyImpl(
|
||||||
|
private val importsImpl: ImportsImpl
|
||||||
|
) : HeaderExclusionPolicy {
|
||||||
|
|
||||||
override fun excludeAll(headerId: HeaderId): Boolean {
|
override fun excludeAll(headerId: HeaderId): Boolean {
|
||||||
return headerId in importsImpl.headerIdToPackage
|
return headerId in importsImpl.headerIdToPackage
|
||||||
|
|||||||
+39
-10
@@ -337,20 +337,49 @@ internal fun buildNativeLibrary(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
val excludeSystemLibs = def.config.excludeSystemLibs
|
val compilation = object : Compilation {
|
||||||
val excludeDependentModules = def.config.excludeDependentModules
|
override val includes = headerFiles
|
||||||
|
override val additionalPreambleLines = def.defHeaderLines
|
||||||
|
override val compilerArgs = compilerOpts + tool.platformCompilerOpts
|
||||||
|
override val language = language
|
||||||
|
}
|
||||||
|
|
||||||
val headerFilterGlobs = def.config.headerFilter
|
val headerFilter: NativeLibraryHeaderFilter
|
||||||
val headerInclusionPolicy = HeadersInclusionPolicyImpl(headerFilterGlobs, imports)
|
val includes: List<String>
|
||||||
|
|
||||||
|
val modules = def.config.modules
|
||||||
|
|
||||||
|
if (modules.isEmpty()) {
|
||||||
|
val excludeDependentModules = def.config.excludeDependentModules
|
||||||
|
|
||||||
|
val headerFilterGlobs = def.config.headerFilter
|
||||||
|
val headerInclusionPolicy = HeaderInclusionPolicyImpl(headerFilterGlobs)
|
||||||
|
|
||||||
|
headerFilter = NativeLibraryHeaderFilter.NameBased(headerInclusionPolicy, excludeDependentModules)
|
||||||
|
includes = headerFiles
|
||||||
|
} else {
|
||||||
|
require(language == Language.OBJECTIVE_C) { "cinterop supports 'modules' only when 'language = Objective-C'" }
|
||||||
|
require(headerFiles.isEmpty()) { "cinterop doesn't support having headers and modules specified at the same time" }
|
||||||
|
require(def.config.headerFilter.isEmpty()) { "cinterop doesn't support 'headerFilter' with 'modules'" }
|
||||||
|
|
||||||
|
val modulesInfo = getModulesInfo(compilation, modules)
|
||||||
|
|
||||||
|
headerFilter = NativeLibraryHeaderFilter.Predefined(modulesInfo.ownHeaders)
|
||||||
|
includes = modulesInfo.topLevelHeaders
|
||||||
|
}
|
||||||
|
|
||||||
|
val excludeSystemLibs = def.config.excludeSystemLibs
|
||||||
|
|
||||||
|
val headerExclusionPolicy = HeaderExclusionPolicyImpl(imports)
|
||||||
|
|
||||||
return NativeLibrary(
|
return NativeLibrary(
|
||||||
includes = headerFiles,
|
includes = includes,
|
||||||
additionalPreambleLines = def.defHeaderLines,
|
additionalPreambleLines = compilation.additionalPreambleLines,
|
||||||
compilerArgs = compilerOpts + tool.platformCompilerOpts,
|
compilerArgs = compilation.compilerArgs,
|
||||||
headerToIdMapper = HeaderToIdMapper(sysRoot = tool.sysRoot),
|
headerToIdMapper = HeaderToIdMapper(sysRoot = tool.sysRoot),
|
||||||
language = language,
|
language = compilation.language,
|
||||||
excludeSystemLibs = excludeSystemLibs,
|
excludeSystemLibs = excludeSystemLibs,
|
||||||
excludeDepdendentModules = excludeDependentModules,
|
headerExclusionPolicy = headerExclusionPolicy,
|
||||||
headerInclusionPolicy = headerInclusionPolicy
|
headerFilter = headerFilter
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,6 +32,10 @@ class DefFile(val file:File?, val config:DefFileConfig, val manifestAddendProper
|
|||||||
properties.getSpaceSeparated("headers")
|
properties.getSpaceSeparated("headers")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val modules by lazy {
|
||||||
|
properties.getSpaceSeparated("modules")
|
||||||
|
}
|
||||||
|
|
||||||
val language by lazy {
|
val language by lazy {
|
||||||
properties.getProperty("language")
|
properties.getProperty("language")
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user