Avoid reparsing C headers in cinterop whenever possible (#3082)
Thus optimize it.
This commit is contained in:
committed by
GitHub
parent
ba19ead041
commit
ab29d5e073
+13
-6
@@ -947,18 +947,23 @@ internal class NativeIndexImpl(val library: NativeLibrary, val verbose: Boolean
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun buildNativeIndexImpl(library: NativeLibrary, verbose: Boolean): NativeIndex {
|
fun buildNativeIndexImpl(library: NativeLibrary, verbose: Boolean): IndexerResult {
|
||||||
val result = NativeIndexImpl(library, verbose)
|
val result = NativeIndexImpl(library, verbose)
|
||||||
indexDeclarations(result)
|
val compilation = indexDeclarations(result)
|
||||||
return result
|
return IndexerResult(result, compilation)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun indexDeclarations(nativeIndex: NativeIndexImpl) {
|
private fun indexDeclarations(nativeIndex: NativeIndexImpl): CompilationWithPCH {
|
||||||
withIndex { index ->
|
withIndex { index ->
|
||||||
val translationUnit = nativeIndex.library.parse(index, options = CXTranslationUnit_DetailedPreprocessingRecord)
|
val translationUnit = nativeIndex.library.parse(
|
||||||
|
index,
|
||||||
|
options = CXTranslationUnit_DetailedPreprocessingRecord or CXTranslationUnit_ForSerialization
|
||||||
|
)
|
||||||
try {
|
try {
|
||||||
translationUnit.ensureNoCompileErrors()
|
translationUnit.ensureNoCompileErrors()
|
||||||
|
|
||||||
|
val compilation = nativeIndex.library.withPrecompiledHeader(translationUnit)
|
||||||
|
|
||||||
val headers = getFilteredHeaders(nativeIndex, index, translationUnit)
|
val headers = getFilteredHeaders(nativeIndex, index, translationUnit)
|
||||||
|
|
||||||
nativeIndex.includedHeaders = headers.map {
|
nativeIndex.includedHeaders = headers.map {
|
||||||
@@ -991,7 +996,9 @@ private fun indexDeclarations(nativeIndex: NativeIndexImpl) {
|
|||||||
CXChildVisitResult.CXChildVisit_Continue
|
CXChildVisitResult.CXChildVisit_Continue
|
||||||
}
|
}
|
||||||
|
|
||||||
findMacros(nativeIndex, translationUnit, headers)
|
findMacros(nativeIndex, compilation, translationUnit, headers)
|
||||||
|
|
||||||
|
return compilation
|
||||||
} finally {
|
} finally {
|
||||||
clang_disposeTranslationUnit(translationUnit)
|
clang_disposeTranslationUnit(translationUnit)
|
||||||
}
|
}
|
||||||
|
|||||||
+13
-10
@@ -23,10 +23,15 @@ import java.io.File
|
|||||||
/**
|
/**
|
||||||
* Finds all "macro constants" and registers them as [NativeIndex.constants] in given index.
|
* Finds all "macro constants" and registers them as [NativeIndex.constants] in given index.
|
||||||
*/
|
*/
|
||||||
internal fun findMacros(nativeIndex: NativeIndexImpl, translationUnit: CXTranslationUnit, headers: Set<CXFile?>) {
|
internal fun findMacros(
|
||||||
|
nativeIndex: NativeIndexImpl,
|
||||||
|
compilation: CompilationWithPCH,
|
||||||
|
translationUnit: CXTranslationUnit,
|
||||||
|
headers: Set<CXFile?>
|
||||||
|
) {
|
||||||
val names = collectMacroNames(nativeIndex, translationUnit, headers)
|
val names = collectMacroNames(nativeIndex, translationUnit, headers)
|
||||||
// TODO: apply user-defined filters.
|
// TODO: apply user-defined filters.
|
||||||
val macros = expandMacros(nativeIndex.library, names, typeConverter = { nativeIndex.convertType(it) })
|
val macros = expandMacros(compilation, names, typeConverter = { nativeIndex.convertType(it) })
|
||||||
|
|
||||||
macros.filterIsInstanceTo(nativeIndex.macroConstants)
|
macros.filterIsInstanceTo(nativeIndex.macroConstants)
|
||||||
macros.filterIsInstanceTo(nativeIndex.wrappedMacros)
|
macros.filterIsInstanceTo(nativeIndex.wrappedMacros)
|
||||||
@@ -38,18 +43,16 @@ private typealias TypeConverter = (CValue<CXType>) -> Type
|
|||||||
* For each name expands the macro with this name declared in the library,
|
* For each name expands the macro with this name declared in the library,
|
||||||
* checking if it gets expanded to a constant expression.
|
* checking if it gets expanded to a constant expression.
|
||||||
*
|
*
|
||||||
|
* Note: in the worst case this method parses the code against the library a lot of times,
|
||||||
|
* so it requires library headers precompiled to significantly speed up the parsing and avoid visiting headers' AST.
|
||||||
|
*
|
||||||
* @return the list of constants.
|
* @return the list of constants.
|
||||||
*/
|
*/
|
||||||
private fun expandMacros(
|
private fun expandMacros(
|
||||||
originalLibrary: NativeLibrary,
|
library: CompilationWithPCH,
|
||||||
names: List<String>,
|
names: List<String>,
|
||||||
typeConverter: TypeConverter
|
typeConverter: TypeConverter
|
||||||
): List<MacroDef> {
|
): List<MacroDef> {
|
||||||
|
|
||||||
// In the worst case code is parsed against the library a lot of times;
|
|
||||||
// so precompile library headers to significantly speed up the parsing and avoid visiting headers' AST:
|
|
||||||
val library = originalLibrary.precompileHeaders()
|
|
||||||
|
|
||||||
withIndex(excludeDeclarationsFromPCH = true) { index ->
|
withIndex(excludeDeclarationsFromPCH = true) { index ->
|
||||||
val sourceFile = library.createTempSource()
|
val sourceFile = library.createTempSource()
|
||||||
val compilerArgs = library.compilerArgs.toMutableList()
|
val compilerArgs = library.compilerArgs.toMutableList()
|
||||||
@@ -94,7 +97,7 @@ private fun expandMacros(
|
|||||||
* As a side effect, modifies the [sourceFile] and reparses the [translationUnit].
|
* As a side effect, modifies the [sourceFile] and reparses the [translationUnit].
|
||||||
*/
|
*/
|
||||||
private fun tryExpandMacros(
|
private fun tryExpandMacros(
|
||||||
library: NativeLibrary,
|
library: CompilationWithPCH,
|
||||||
translationUnit: CXTranslationUnit,
|
translationUnit: CXTranslationUnit,
|
||||||
sourceFile: File,
|
sourceFile: File,
|
||||||
names: List<String>,
|
names: List<String>,
|
||||||
@@ -158,7 +161,7 @@ private const val CODE_SNIPPET_FUNCTION_NAME_PREFIX = "kni_indexer_function_"
|
|||||||
* generate a bridge for this macro.
|
* generate a bridge for this macro.
|
||||||
* - Otherwise the macro is skipped.
|
* - Otherwise the macro is skipped.
|
||||||
*/
|
*/
|
||||||
private fun reparseWithCodeSnippets(library: NativeLibrary,
|
private fun reparseWithCodeSnippets(library: CompilationWithPCH,
|
||||||
translationUnit: CXTranslationUnit, sourceFile: File,
|
translationUnit: CXTranslationUnit, sourceFile: File,
|
||||||
names: List<String>) {
|
names: List<String>) {
|
||||||
|
|
||||||
|
|||||||
+3
-3
@@ -53,9 +53,9 @@ internal open class ModularCompilation(compilation: Compilation): Compilation by
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun getModulesASTFiles(index: CXIndex, compilation: ModularCompilation, modules: List<String>): List<String> {
|
private fun getModulesASTFiles(index: CXIndex, compilation: ModularCompilation, modules: List<String>): List<String> {
|
||||||
val compilationWithImports = object : Compilation by compilation {
|
val compilationWithImports = compilation.copy(
|
||||||
override val additionalPreambleLines = modules.map { "@import $it;" } + compilation.additionalPreambleLines
|
additionalPreambleLines = modules.map { "@import $it;" } + compilation.additionalPreambleLines
|
||||||
}
|
)
|
||||||
|
|
||||||
val result = linkedSetOf<String>()
|
val result = linkedSetOf<String>()
|
||||||
|
|
||||||
|
|||||||
+19
-2
@@ -57,7 +57,22 @@ interface Compilation {
|
|||||||
val language: Language
|
val language: Language
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: consider replacing inheritance by composition and turning Compilation into final class.
|
data class CompilationWithPCH(
|
||||||
|
override val compilerArgs: List<String>,
|
||||||
|
override val language: Language
|
||||||
|
) : Compilation {
|
||||||
|
|
||||||
|
constructor(compilerArgs: List<String>, precompiledHeader: String, language: Language)
|
||||||
|
: this(compilerArgs + listOf("-include-pch", precompiledHeader), language)
|
||||||
|
|
||||||
|
override val includes: List<String>
|
||||||
|
get() = emptyList()
|
||||||
|
|
||||||
|
override val additionalPreambleLines: List<String>
|
||||||
|
get() = emptyList()
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Compilation hierarchy seems to require some refactoring.
|
||||||
|
|
||||||
data class NativeLibrary(override val includes: List<String>,
|
data class NativeLibrary(override val includes: List<String>,
|
||||||
override val additionalPreambleLines: List<String>,
|
override val additionalPreambleLines: List<String>,
|
||||||
@@ -68,10 +83,12 @@ data class NativeLibrary(override val includes: List<String>,
|
|||||||
val headerExclusionPolicy: HeaderExclusionPolicy,
|
val headerExclusionPolicy: HeaderExclusionPolicy,
|
||||||
val headerFilter: NativeLibraryHeaderFilter) : Compilation
|
val headerFilter: NativeLibraryHeaderFilter) : Compilation
|
||||||
|
|
||||||
|
data class IndexerResult(val index: NativeIndex, val compilation: CompilationWithPCH)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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).
|
||||||
*/
|
*/
|
||||||
fun buildNativeIndex(library: NativeLibrary, verbose: Boolean): NativeIndex = buildNativeIndexImpl(library, verbose)
|
fun buildNativeIndex(library: NativeLibrary, verbose: Boolean): IndexerResult = buildNativeIndexImpl(library, verbose)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class describes the IR of definitions from C header file(s).
|
* This class describes the IR of definitions from C header file(s).
|
||||||
|
|||||||
+35
-20
@@ -241,30 +241,46 @@ internal fun Compilation.createTempSource(): File {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun Compilation.copy(
|
||||||
|
includes: List<String> = this.includes,
|
||||||
|
additionalPreambleLines: List<String> = this.additionalPreambleLines,
|
||||||
|
compilerArgs: List<String> = this.compilerArgs,
|
||||||
|
language: Language = this.language
|
||||||
|
): Compilation = CompilationImpl(
|
||||||
|
includes = includes,
|
||||||
|
additionalPreambleLines = additionalPreambleLines,
|
||||||
|
compilerArgs = compilerArgs,
|
||||||
|
language = language
|
||||||
|
)
|
||||||
|
|
||||||
|
data class CompilationImpl(
|
||||||
|
override val includes: List<String>,
|
||||||
|
override val additionalPreambleLines: List<String>,
|
||||||
|
override val compilerArgs: List<String>,
|
||||||
|
override val language: Language
|
||||||
|
) : Compilation
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Precompiles the headers of this library.
|
* Precompiles the headers of this library.
|
||||||
*
|
*
|
||||||
* @return the library which includes the precompiled header instead of original ones.
|
* @return the library which includes the precompiled header instead of original ones.
|
||||||
*/
|
*/
|
||||||
internal fun NativeLibrary.precompileHeaders(): NativeLibrary {
|
fun Compilation.precompileHeaders(): CompilationWithPCH = withIndex { index ->
|
||||||
val precompiledHeader = createTempFile(suffix = ".pch").apply { this.deleteOnExit() }
|
val options = CXTranslationUnit_ForSerialization
|
||||||
|
val translationUnit = this.parse(index, options)
|
||||||
withIndex { index ->
|
try {
|
||||||
val options = CXTranslationUnit_ForSerialization
|
translationUnit.ensureNoCompileErrors()
|
||||||
val translationUnit = this.parse(index, options)
|
withPrecompiledHeader(translationUnit)
|
||||||
try {
|
} finally {
|
||||||
translationUnit.ensureNoCompileErrors()
|
clang_disposeTranslationUnit(translationUnit)
|
||||||
clang_saveTranslationUnit(translationUnit, precompiledHeader.absolutePath, 0)
|
|
||||||
} finally {
|
|
||||||
clang_disposeTranslationUnit(translationUnit)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return this.copy(
|
internal fun Compilation.withPrecompiledHeader(translationUnit: CXTranslationUnit): CompilationWithPCH {
|
||||||
includes = emptyList(),
|
val precompiledHeader = createTempFile(suffix = ".pch").apply { this.deleteOnExit() }
|
||||||
additionalPreambleLines = emptyList(),
|
clang_saveTranslationUnit(translationUnit, precompiledHeader.absolutePath, 0)
|
||||||
compilerArgs = this.compilerArgs + listOf("-include-pch", precompiledHeader.absolutePath)
|
|
||||||
)
|
return CompilationWithPCH(this.compilerArgs, precompiledHeader.absolutePath, this.language)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun NativeLibrary.includesDeclaration(cursor: CValue<CXCursor>): Boolean {
|
internal fun NativeLibrary.includesDeclaration(cursor: CValue<CXCursor>): Boolean {
|
||||||
@@ -289,10 +305,9 @@ internal fun CXTranslationUnit.getErrorLineNumbers(): Sequence<Int> =
|
|||||||
/**
|
/**
|
||||||
* For each list of lines, checks if the code fragment composed from these lines is compilable against given library.
|
* For each list of lines, checks if the code fragment composed from these lines is compilable against given library.
|
||||||
*/
|
*/
|
||||||
fun List<List<String>>.mapFragmentIsCompilable(originalLibrary: NativeLibrary): List<Boolean> {
|
fun List<List<String>>.mapFragmentIsCompilable(originalLibrary: CompilationWithPCH): List<Boolean> {
|
||||||
val library = originalLibrary
|
val library: CompilationWithPCH = originalLibrary
|
||||||
.copy(compilerArgs = originalLibrary.compilerArgs + "-ferror-limit=0")
|
.copy(compilerArgs = originalLibrary.compilerArgs + "-ferror-limit=0")
|
||||||
.precompileHeaders()
|
|
||||||
|
|
||||||
val indicesOfNonCompilable = mutableSetOf<Int>()
|
val indicesOfNonCompilable = mutableSetOf<Int>()
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -17,15 +17,15 @@
|
|||||||
package org.jetbrains.kotlin.native.interop.gen
|
package org.jetbrains.kotlin.native.interop.gen
|
||||||
|
|
||||||
import org.jetbrains.kotlin.native.interop.gen.jvm.KotlinPlatform
|
import org.jetbrains.kotlin.native.interop.gen.jvm.KotlinPlatform
|
||||||
|
import org.jetbrains.kotlin.native.interop.indexer.CompilationWithPCH
|
||||||
import org.jetbrains.kotlin.native.interop.indexer.Language
|
import org.jetbrains.kotlin.native.interop.indexer.Language
|
||||||
import org.jetbrains.kotlin.native.interop.indexer.NativeLibrary
|
|
||||||
import org.jetbrains.kotlin.native.interop.indexer.mapFragmentIsCompilable
|
import org.jetbrains.kotlin.native.interop.indexer.mapFragmentIsCompilable
|
||||||
|
|
||||||
class SimpleBridgeGeneratorImpl(
|
class SimpleBridgeGeneratorImpl(
|
||||||
private val platform: KotlinPlatform,
|
private val platform: KotlinPlatform,
|
||||||
private val pkgName: String,
|
private val pkgName: String,
|
||||||
private val jvmFileClassName: String,
|
private val jvmFileClassName: String,
|
||||||
private val libraryForCStubs: NativeLibrary,
|
private val libraryForCStubs: CompilationWithPCH,
|
||||||
override val topLevelNativeScope: NativeScope,
|
override val topLevelNativeScope: NativeScope,
|
||||||
private val topLevelKotlinScope: KotlinScope
|
private val topLevelKotlinScope: KotlinScope
|
||||||
) : SimpleBridgeGenerator {
|
) : SimpleBridgeGenerator {
|
||||||
|
|||||||
+2
-2
@@ -17,13 +17,13 @@
|
|||||||
package org.jetbrains.kotlin.native.interop.gen.jvm
|
package org.jetbrains.kotlin.native.interop.gen.jvm
|
||||||
|
|
||||||
import org.jetbrains.kotlin.konan.target.KonanTarget
|
import org.jetbrains.kotlin.konan.target.KonanTarget
|
||||||
import org.jetbrains.kotlin.native.interop.indexer.NativeLibrary
|
import org.jetbrains.kotlin.native.interop.indexer.CompilationWithPCH
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Describes the native library and the options for adjusting the Kotlin API to be generated for this library.
|
* Describes the native library and the options for adjusting the Kotlin API to be generated for this library.
|
||||||
*/
|
*/
|
||||||
class InteropConfiguration(
|
class InteropConfiguration(
|
||||||
val library: NativeLibrary,
|
val library: CompilationWithPCH,
|
||||||
val pkgName: String,
|
val pkgName: String,
|
||||||
val excludedFunctions: Set<String>,
|
val excludedFunctions: Set<String>,
|
||||||
val excludedMacros: Set<String>,
|
val excludedMacros: Set<String>,
|
||||||
|
|||||||
+1
-1
@@ -965,7 +965,7 @@ class StubGenerator(
|
|||||||
Language.C -> emptyList()
|
Language.C -> emptyList()
|
||||||
Language.OBJECTIVE_C -> listOf("void objc_terminate();")
|
Language.OBJECTIVE_C -> listOf("void objc_terminate();")
|
||||||
}
|
}
|
||||||
)
|
).precompileHeaders()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Produces to [out] the contents of C source file to be compiled into native lib used for Kotlin bindings impl.
|
* Produces to [out] the contents of C source file to be compiled into native lib used for Kotlin bindings impl.
|
||||||
|
|||||||
+9
-9
@@ -220,8 +220,10 @@ private fun processCLib(args: Array<String>, additionalArgs: Map<String, Any> =
|
|||||||
|
|
||||||
val library = buildNativeLibrary(tool, def, argParser, imports)
|
val library = buildNativeLibrary(tool, def, argParser, imports)
|
||||||
|
|
||||||
|
val (nativeIndex, compilation) = buildNativeIndex(library, verbose)
|
||||||
|
|
||||||
val configuration = InteropConfiguration(
|
val configuration = InteropConfiguration(
|
||||||
library = library,
|
library = compilation,
|
||||||
pkgName = outKtPkg,
|
pkgName = outKtPkg,
|
||||||
excludedFunctions = excludedFunctions,
|
excludedFunctions = excludedFunctions,
|
||||||
excludedMacros = excludedMacros,
|
excludedMacros = excludedMacros,
|
||||||
@@ -233,8 +235,6 @@ private fun processCLib(args: Array<String>, additionalArgs: Map<String, Any> =
|
|||||||
target = tool.target
|
target = tool.target
|
||||||
)
|
)
|
||||||
|
|
||||||
val nativeIndex = buildNativeIndex(library, verbose)
|
|
||||||
|
|
||||||
val gen = StubGenerator(nativeIndex, configuration, libName, verbose, flavor, imports)
|
val gen = StubGenerator(nativeIndex, configuration, libName, verbose, flavor, imports)
|
||||||
|
|
||||||
outKtFile.parentFile.mkdirs()
|
outKtFile.parentFile.mkdirs()
|
||||||
@@ -330,12 +330,12 @@ internal fun buildNativeLibrary(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
val compilation = object : Compilation {
|
val compilation = CompilationImpl(
|
||||||
override val includes = headerFiles
|
includes = headerFiles,
|
||||||
override val additionalPreambleLines = def.defHeaderLines
|
additionalPreambleLines = def.defHeaderLines,
|
||||||
override val compilerArgs = compilerOpts + tool.platformCompilerOpts
|
compilerArgs = compilerOpts + tool.platformCompilerOpts,
|
||||||
override val language = language
|
language = language
|
||||||
}
|
)
|
||||||
|
|
||||||
val headerFilter: NativeLibraryHeaderFilter
|
val headerFilter: NativeLibraryHeaderFilter
|
||||||
val includes: List<String>
|
val includes: List<String>
|
||||||
|
|||||||
Reference in New Issue
Block a user