diff --git a/Interop/Indexer/src/main/kotlin/org/jetbrains/kotlin/native/interop/indexer/Indexer.kt b/Interop/Indexer/src/main/kotlin/org/jetbrains/kotlin/native/interop/indexer/Indexer.kt index a9f490e081d..09b1b0ba4d4 100644 --- a/Interop/Indexer/src/main/kotlin/org/jetbrains/kotlin/native/interop/indexer/Indexer.kt +++ b/Interop/Indexer/src/main/kotlin/org/jetbrains/kotlin/native/interop/indexer/Indexer.kt @@ -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) - indexDeclarations(result) - return result + val compilation = indexDeclarations(result) + return IndexerResult(result, compilation) } -private fun indexDeclarations(nativeIndex: NativeIndexImpl) { +private fun indexDeclarations(nativeIndex: NativeIndexImpl): CompilationWithPCH { withIndex { index -> - val translationUnit = nativeIndex.library.parse(index, options = CXTranslationUnit_DetailedPreprocessingRecord) + val translationUnit = nativeIndex.library.parse( + index, + options = CXTranslationUnit_DetailedPreprocessingRecord or CXTranslationUnit_ForSerialization + ) try { translationUnit.ensureNoCompileErrors() + val compilation = nativeIndex.library.withPrecompiledHeader(translationUnit) + val headers = getFilteredHeaders(nativeIndex, index, translationUnit) nativeIndex.includedHeaders = headers.map { @@ -991,7 +996,9 @@ private fun indexDeclarations(nativeIndex: NativeIndexImpl) { CXChildVisitResult.CXChildVisit_Continue } - findMacros(nativeIndex, translationUnit, headers) + findMacros(nativeIndex, compilation, translationUnit, headers) + + return compilation } finally { clang_disposeTranslationUnit(translationUnit) } diff --git a/Interop/Indexer/src/main/kotlin/org/jetbrains/kotlin/native/interop/indexer/MacroConstants.kt b/Interop/Indexer/src/main/kotlin/org/jetbrains/kotlin/native/interop/indexer/MacroConstants.kt index 5da04082f8a..f83443c3344 100644 --- a/Interop/Indexer/src/main/kotlin/org/jetbrains/kotlin/native/interop/indexer/MacroConstants.kt +++ b/Interop/Indexer/src/main/kotlin/org/jetbrains/kotlin/native/interop/indexer/MacroConstants.kt @@ -23,10 +23,15 @@ import java.io.File /** * Finds all "macro constants" and registers them as [NativeIndex.constants] in given index. */ -internal fun findMacros(nativeIndex: NativeIndexImpl, translationUnit: CXTranslationUnit, headers: Set) { +internal fun findMacros( + nativeIndex: NativeIndexImpl, + compilation: CompilationWithPCH, + translationUnit: CXTranslationUnit, + headers: Set +) { val names = collectMacroNames(nativeIndex, translationUnit, headers) // 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.wrappedMacros) @@ -38,18 +43,16 @@ private typealias TypeConverter = (CValue) -> Type * For each name expands the macro with this name declared in the library, * 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. */ private fun expandMacros( - originalLibrary: NativeLibrary, + library: CompilationWithPCH, names: List, typeConverter: TypeConverter ): List { - - // 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 -> val sourceFile = library.createTempSource() val compilerArgs = library.compilerArgs.toMutableList() @@ -94,7 +97,7 @@ private fun expandMacros( * As a side effect, modifies the [sourceFile] and reparses the [translationUnit]. */ private fun tryExpandMacros( - library: NativeLibrary, + library: CompilationWithPCH, translationUnit: CXTranslationUnit, sourceFile: File, names: List, @@ -158,7 +161,7 @@ private const val CODE_SNIPPET_FUNCTION_NAME_PREFIX = "kni_indexer_function_" * generate a bridge for this macro. * - Otherwise the macro is skipped. */ -private fun reparseWithCodeSnippets(library: NativeLibrary, +private fun reparseWithCodeSnippets(library: CompilationWithPCH, translationUnit: CXTranslationUnit, sourceFile: File, names: List) { diff --git a/Interop/Indexer/src/main/kotlin/org/jetbrains/kotlin/native/interop/indexer/ModuleSupport.kt b/Interop/Indexer/src/main/kotlin/org/jetbrains/kotlin/native/interop/indexer/ModuleSupport.kt index 9c649fe8229..b603ae69c2e 100644 --- a/Interop/Indexer/src/main/kotlin/org/jetbrains/kotlin/native/interop/indexer/ModuleSupport.kt +++ b/Interop/Indexer/src/main/kotlin/org/jetbrains/kotlin/native/interop/indexer/ModuleSupport.kt @@ -53,9 +53,9 @@ internal open class ModularCompilation(compilation: Compilation): Compilation by } private fun getModulesASTFiles(index: CXIndex, compilation: ModularCompilation, modules: List): List { - val compilationWithImports = object : Compilation by compilation { - override val additionalPreambleLines = modules.map { "@import $it;" } + compilation.additionalPreambleLines - } + val compilationWithImports = compilation.copy( + additionalPreambleLines = modules.map { "@import $it;" } + compilation.additionalPreambleLines + ) val result = linkedSetOf() diff --git a/Interop/Indexer/src/main/kotlin/org/jetbrains/kotlin/native/interop/indexer/NativeIndex.kt b/Interop/Indexer/src/main/kotlin/org/jetbrains/kotlin/native/interop/indexer/NativeIndex.kt index 2652ad42784..b209520f9fd 100644 --- a/Interop/Indexer/src/main/kotlin/org/jetbrains/kotlin/native/interop/indexer/NativeIndex.kt +++ b/Interop/Indexer/src/main/kotlin/org/jetbrains/kotlin/native/interop/indexer/NativeIndex.kt @@ -57,7 +57,22 @@ interface Compilation { val language: Language } -// TODO: consider replacing inheritance by composition and turning Compilation into final class. +data class CompilationWithPCH( + override val compilerArgs: List, + override val language: Language +) : Compilation { + + constructor(compilerArgs: List, precompiledHeader: String, language: Language) + : this(compilerArgs + listOf("-include-pch", precompiledHeader), language) + + override val includes: List + get() = emptyList() + + override val additionalPreambleLines: List + get() = emptyList() +} + +// TODO: Compilation hierarchy seems to require some refactoring. data class NativeLibrary(override val includes: List, override val additionalPreambleLines: List, @@ -68,10 +83,12 @@ data class NativeLibrary(override val includes: List, val headerExclusionPolicy: HeaderExclusionPolicy, 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). */ -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). diff --git a/Interop/Indexer/src/main/kotlin/org/jetbrains/kotlin/native/interop/indexer/Utils.kt b/Interop/Indexer/src/main/kotlin/org/jetbrains/kotlin/native/interop/indexer/Utils.kt index 1438ba79f93..ff912da3d88 100644 --- a/Interop/Indexer/src/main/kotlin/org/jetbrains/kotlin/native/interop/indexer/Utils.kt +++ b/Interop/Indexer/src/main/kotlin/org/jetbrains/kotlin/native/interop/indexer/Utils.kt @@ -241,30 +241,46 @@ internal fun Compilation.createTempSource(): File { return result } +fun Compilation.copy( + includes: List = this.includes, + additionalPreambleLines: List = this.additionalPreambleLines, + compilerArgs: List = this.compilerArgs, + language: Language = this.language +): Compilation = CompilationImpl( + includes = includes, + additionalPreambleLines = additionalPreambleLines, + compilerArgs = compilerArgs, + language = language +) + +data class CompilationImpl( + override val includes: List, + override val additionalPreambleLines: List, + override val compilerArgs: List, + override val language: Language +) : Compilation + /** * Precompiles the headers of this library. * * @return the library which includes the precompiled header instead of original ones. */ -internal fun NativeLibrary.precompileHeaders(): NativeLibrary { - val precompiledHeader = createTempFile(suffix = ".pch").apply { this.deleteOnExit() } - - withIndex { index -> - val options = CXTranslationUnit_ForSerialization - val translationUnit = this.parse(index, options) - try { - translationUnit.ensureNoCompileErrors() - clang_saveTranslationUnit(translationUnit, precompiledHeader.absolutePath, 0) - } finally { - clang_disposeTranslationUnit(translationUnit) - } +fun Compilation.precompileHeaders(): CompilationWithPCH = withIndex { index -> + val options = CXTranslationUnit_ForSerialization + val translationUnit = this.parse(index, options) + try { + translationUnit.ensureNoCompileErrors() + withPrecompiledHeader(translationUnit) + } finally { + clang_disposeTranslationUnit(translationUnit) } +} - return this.copy( - includes = emptyList(), - additionalPreambleLines = emptyList(), - compilerArgs = this.compilerArgs + listOf("-include-pch", precompiledHeader.absolutePath) - ) +internal fun Compilation.withPrecompiledHeader(translationUnit: CXTranslationUnit): CompilationWithPCH { + val precompiledHeader = createTempFile(suffix = ".pch").apply { this.deleteOnExit() } + clang_saveTranslationUnit(translationUnit, precompiledHeader.absolutePath, 0) + + return CompilationWithPCH(this.compilerArgs, precompiledHeader.absolutePath, this.language) } internal fun NativeLibrary.includesDeclaration(cursor: CValue): Boolean { @@ -289,10 +305,9 @@ internal fun CXTranslationUnit.getErrorLineNumbers(): Sequence = /** * For each list of lines, checks if the code fragment composed from these lines is compilable against given library. */ -fun List>.mapFragmentIsCompilable(originalLibrary: NativeLibrary): List { - val library = originalLibrary +fun List>.mapFragmentIsCompilable(originalLibrary: CompilationWithPCH): List { + val library: CompilationWithPCH = originalLibrary .copy(compilerArgs = originalLibrary.compilerArgs + "-ferror-limit=0") - .precompileHeaders() val indicesOfNonCompilable = mutableSetOf() diff --git a/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/SimpleBridgeGeneratorImpl.kt b/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/SimpleBridgeGeneratorImpl.kt index 589ed5769b2..29a806f6c11 100644 --- a/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/SimpleBridgeGeneratorImpl.kt +++ b/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/SimpleBridgeGeneratorImpl.kt @@ -17,15 +17,15 @@ package org.jetbrains.kotlin.native.interop.gen 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.NativeLibrary import org.jetbrains.kotlin.native.interop.indexer.mapFragmentIsCompilable class SimpleBridgeGeneratorImpl( private val platform: KotlinPlatform, private val pkgName: String, private val jvmFileClassName: String, - private val libraryForCStubs: NativeLibrary, + private val libraryForCStubs: CompilationWithPCH, override val topLevelNativeScope: NativeScope, private val topLevelKotlinScope: KotlinScope ) : SimpleBridgeGenerator { diff --git a/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/jvm/InteropConfiguration.kt b/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/jvm/InteropConfiguration.kt index b83a5112575..3d465d02b0e 100644 --- a/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/jvm/InteropConfiguration.kt +++ b/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/jvm/InteropConfiguration.kt @@ -17,13 +17,13 @@ package org.jetbrains.kotlin.native.interop.gen.jvm 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. */ class InteropConfiguration( - val library: NativeLibrary, + val library: CompilationWithPCH, val pkgName: String, val excludedFunctions: Set, val excludedMacros: Set, diff --git a/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/jvm/StubGenerator.kt b/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/jvm/StubGenerator.kt index d5c65027b75..c09c90d0220 100644 --- a/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/jvm/StubGenerator.kt +++ b/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/jvm/StubGenerator.kt @@ -965,7 +965,7 @@ class StubGenerator( Language.C -> emptyList() 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. diff --git a/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/jvm/main.kt b/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/jvm/main.kt index 27d838b694c..79387f4b20a 100644 --- a/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/jvm/main.kt +++ b/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/jvm/main.kt @@ -220,8 +220,10 @@ private fun processCLib(args: Array, additionalArgs: Map = val library = buildNativeLibrary(tool, def, argParser, imports) + val (nativeIndex, compilation) = buildNativeIndex(library, verbose) + val configuration = InteropConfiguration( - library = library, + library = compilation, pkgName = outKtPkg, excludedFunctions = excludedFunctions, excludedMacros = excludedMacros, @@ -233,8 +235,6 @@ private fun processCLib(args: Array, additionalArgs: Map = target = tool.target ) - val nativeIndex = buildNativeIndex(library, verbose) - val gen = StubGenerator(nativeIndex, configuration, libName, verbose, flavor, imports) outKtFile.parentFile.mkdirs() @@ -330,12 +330,12 @@ internal fun buildNativeLibrary( }) } - val compilation = object : Compilation { - override val includes = headerFiles - override val additionalPreambleLines = def.defHeaderLines - override val compilerArgs = compilerOpts + tool.platformCompilerOpts - override val language = language - } + val compilation = CompilationImpl( + includes = headerFiles, + additionalPreambleLines = def.defHeaderLines, + compilerArgs = compilerOpts + tool.platformCompilerOpts, + language = language + ) val headerFilter: NativeLibraryHeaderFilter val includes: List