Interop: extract constants defined as (preprocessor) macros
Support all macros extending to integer and floating-point constant expressions. Also do some refactoring.
This commit is contained in:
committed by
SvyatoslavScherbina
parent
01dd86d3b5
commit
f8d54fc8f7
+65
-68
@@ -4,7 +4,6 @@ import clang.*
|
|||||||
import clang.CXIdxEntityKind.*
|
import clang.CXIdxEntityKind.*
|
||||||
import clang.CXTypeKind.*
|
import clang.CXTypeKind.*
|
||||||
import kotlinx.cinterop.*
|
import kotlinx.cinterop.*
|
||||||
import java.io.File
|
|
||||||
|
|
||||||
private class StructDeclImpl(spelling: String) : StructDecl(spelling) {
|
private class StructDeclImpl(spelling: String) : StructDecl(spelling) {
|
||||||
override var def: StructDefImpl? = null
|
override var def: StructDefImpl? = null
|
||||||
@@ -20,21 +19,21 @@ private class EnumDefImpl(spelling: String, type: PrimitiveType) : EnumDef(spell
|
|||||||
override val values = mutableListOf<EnumValue>()
|
override val values = mutableListOf<EnumValue>()
|
||||||
}
|
}
|
||||||
|
|
||||||
private class NativeIndexImpl : NativeIndex() {
|
internal class NativeIndexImpl : NativeIndex() {
|
||||||
|
|
||||||
private data class DeclarationID(val usr: String)
|
private data class DeclarationID(val usr: String)
|
||||||
|
|
||||||
val structById = mutableMapOf<DeclarationID, StructDeclImpl>()
|
private val structById = mutableMapOf<DeclarationID, StructDeclImpl>()
|
||||||
|
|
||||||
override val structs: List<StructDecl>
|
override val structs: List<StructDecl>
|
||||||
get() = structById.values.toList()
|
get() = structById.values.toList()
|
||||||
|
|
||||||
val enumById = mutableMapOf<DeclarationID, EnumDefImpl>()
|
private val enumById = mutableMapOf<DeclarationID, EnumDefImpl>()
|
||||||
|
|
||||||
override val enums: List<EnumDef>
|
override val enums: List<EnumDef>
|
||||||
get() = enumById.values.toList()
|
get() = enumById.values.toList()
|
||||||
|
|
||||||
val typedefById = mutableMapOf<DeclarationID, TypedefDef>()
|
private val typedefById = mutableMapOf<DeclarationID, TypedefDef>()
|
||||||
|
|
||||||
override val typedefs: List<TypedefDef>
|
override val typedefs: List<TypedefDef>
|
||||||
get() = typedefById.values.toList()
|
get() = typedefById.values.toList()
|
||||||
@@ -44,12 +43,14 @@ private class NativeIndexImpl : NativeIndex() {
|
|||||||
override val functions: List<FunctionDecl>
|
override val functions: List<FunctionDecl>
|
||||||
get() = functionByName.values.toList()
|
get() = functionByName.values.toList()
|
||||||
|
|
||||||
fun getDeclarationId(cursor: CXCursor): DeclarationID = memScoped {
|
override val macroConstants = mutableListOf<ConstantDef>()
|
||||||
|
|
||||||
|
private fun getDeclarationId(cursor: CXCursor): DeclarationID = memScoped {
|
||||||
val usr = clang_getCursorUSR(cursor, memScope).convertAndDispose()
|
val usr = clang_getCursorUSR(cursor, memScope).convertAndDispose()
|
||||||
DeclarationID(usr)
|
DeclarationID(usr)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getStructDeclAt(cursor: CXCursor): StructDeclImpl {
|
private fun getStructDeclAt(cursor: CXCursor): StructDeclImpl {
|
||||||
val declId = getDeclarationId(cursor)
|
val declId = getDeclarationId(cursor)
|
||||||
|
|
||||||
return structById.getOrPut(declId) {
|
return structById.getOrPut(declId) {
|
||||||
@@ -62,7 +63,7 @@ private class NativeIndexImpl : NativeIndex() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getEnumDefAt(cursor: CXCursor): EnumDefImpl {
|
private fun getEnumDefAt(cursor: CXCursor): EnumDefImpl {
|
||||||
if (clang_isCursorDefinition(cursor) == 0) {
|
if (clang_isCursorDefinition(cursor) == 0) {
|
||||||
TODO("support enum forward declarations")
|
TODO("support enum forward declarations")
|
||||||
}
|
}
|
||||||
@@ -101,7 +102,7 @@ private class NativeIndexImpl : NativeIndex() {
|
|||||||
|
|
||||||
fun getTypedef(type: CXType): Type = memScoped {
|
fun getTypedef(type: CXType): Type = memScoped {
|
||||||
val declCursor = clang_getTypeDeclaration(type, memScope)
|
val declCursor = clang_getTypeDeclaration(type, memScope)
|
||||||
val name = clang_getCursorSpelling(declCursor, memScope).convertAndDispose()
|
val name = getCursorSpelling(declCursor)
|
||||||
|
|
||||||
val underlying = convertType(clang_getTypedefDeclUnderlyingType(declCursor, memScope))
|
val underlying = convertType(clang_getTypedefDeclUnderlyingType(declCursor, memScope))
|
||||||
|
|
||||||
@@ -160,7 +161,16 @@ private class NativeIndexImpl : NativeIndex() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun convertCursorType(cursor: CXCursor) = memScoped {
|
||||||
|
convertType(clang_getCursorType(cursor, memScope))
|
||||||
|
}
|
||||||
|
|
||||||
fun convertType(type: CXType): Type = memScoped {
|
fun convertType(type: CXType): Type = memScoped {
|
||||||
|
val primitiveType = convertUnqualifiedPrimitiveType(type)
|
||||||
|
if (primitiveType != UnsupportedType) {
|
||||||
|
return primitiveType
|
||||||
|
}
|
||||||
|
|
||||||
val kind = type.kind.value
|
val kind = type.kind.value
|
||||||
return when (kind) {
|
return when (kind) {
|
||||||
CXType_Elaborated -> convertType(clang_Type_getNamedType(type, memScope))
|
CXType_Elaborated -> convertType(clang_Type_getNamedType(type, memScope))
|
||||||
@@ -174,24 +184,8 @@ private class NativeIndexImpl : NativeIndex() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: is e.g. CXType_Int guaranteed to be int32_t?
|
|
||||||
CXType_Void -> VoidType
|
CXType_Void -> VoidType
|
||||||
|
|
||||||
CXType_Char_U, CXType_UChar -> UInt8Type
|
|
||||||
CXType_Char_S, CXType_SChar -> Int8Type
|
|
||||||
|
|
||||||
CXType_UShort -> UInt16Type
|
|
||||||
CXType_Short -> Int16Type
|
|
||||||
|
|
||||||
CXType_UInt -> UInt32Type
|
|
||||||
CXType_Int -> Int32Type
|
|
||||||
|
|
||||||
CXType_ULong -> UIntPtrType
|
|
||||||
CXType_Long -> IntPtrType
|
|
||||||
|
|
||||||
CXType_ULongLong -> UInt64Type
|
|
||||||
CXType_LongLong -> Int64Type
|
|
||||||
|
|
||||||
CXType_Typedef -> getTypedef(type)
|
CXType_Typedef -> getTypedef(type)
|
||||||
|
|
||||||
CXType_Record -> RecordType(getStructDeclAt(clang_getTypeDeclaration(type, memScope)))
|
CXType_Record -> RecordType(getStructDeclAt(clang_getTypeDeclaration(type, memScope)))
|
||||||
@@ -236,7 +230,7 @@ private class NativeIndexImpl : NativeIndex() {
|
|||||||
when (kind) {
|
when (kind) {
|
||||||
CXIdxEntity_Field -> {
|
CXIdxEntity_Field -> {
|
||||||
val name = entityName!!
|
val name = entityName!!
|
||||||
val type = convertType(clang_getCursorType(cursor, memScope))
|
val type = convertCursorType(cursor)
|
||||||
val offset = clang_Cursor_getOffsetOfField(cursor)
|
val offset = clang_Cursor_getOffsetOfField(cursor)
|
||||||
|
|
||||||
val container = info.semanticContainer.pointed!!
|
val container = info.semanticContainer.pointed!!
|
||||||
@@ -249,7 +243,7 @@ private class NativeIndexImpl : NativeIndex() {
|
|||||||
if (clang_isCursorDefinition(cursor) != 0) {
|
if (clang_isCursorDefinition(cursor) != 0) {
|
||||||
val type = clang_getCursorType(cursor, memScope)
|
val type = clang_getCursorType(cursor, memScope)
|
||||||
val size = clang_Type_getSizeOf(type)
|
val size = clang_Type_getSizeOf(type)
|
||||||
val align = clang_Type_getAlignOf(clang_getCursorType(cursor, memScope)).toInt()
|
val align = clang_Type_getAlignOf(type).toInt()
|
||||||
val hasNaturalLayout = structHasNaturalLayout(cursor)
|
val hasNaturalLayout = structHasNaturalLayout(cursor)
|
||||||
structDecl.def = StructDefImpl(size, align, structDecl, hasNaturalLayout)
|
structDecl.def = StructDefImpl(size, align, structDecl, hasNaturalLayout)
|
||||||
}
|
}
|
||||||
@@ -261,8 +255,8 @@ private class NativeIndexImpl : NativeIndex() {
|
|||||||
val argNum = clang_Cursor_getNumArguments(cursor)
|
val argNum = clang_Cursor_getNumArguments(cursor)
|
||||||
val args = (0 .. argNum - 1).map {
|
val args = (0 .. argNum - 1).map {
|
||||||
val argCursor = clang_Cursor_getArgument(cursor, it, memScope)
|
val argCursor = clang_Cursor_getArgument(cursor, it, memScope)
|
||||||
val argName = clang_getCursorSpelling(argCursor, memScope).convertAndDispose()
|
val argName = getCursorSpelling(argCursor)
|
||||||
val type = convertType(clang_getCursorType(argCursor, memScope))
|
val type = convertCursorType(argCursor)
|
||||||
Parameter(argName, type)
|
Parameter(argName, type)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -293,50 +287,53 @@ private class NativeIndexImpl : NativeIndex() {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun CXString.convertAndDispose(): String {
|
fun buildNativeIndexImpl(library: NativeLibrary): NativeIndex {
|
||||||
try {
|
val result = NativeIndexImpl()
|
||||||
return clang_getCString(this)!!.asCString().toString()
|
indexDeclarations(library, result)
|
||||||
} finally {
|
findMacroConstants(library, result)
|
||||||
clang_disposeString(this)
|
return result
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun buildNativeIndexImpl(headerFile: File, args: List<String>): NativeIndex {
|
private fun indexDeclarations(library: NativeLibrary, nativeIndex: NativeIndexImpl) {
|
||||||
// TODO: dispose all allocated memory and resources
|
val index = clang_createIndex(0, 0)!!
|
||||||
val args1 = args.map { CString.fromString(it, nativeHeap)!!.asCharPtr() }.toTypedArray()
|
val indexAction = clang_IndexAction_create(index)!!
|
||||||
|
|
||||||
val index = clang_createIndex(0, 0)
|
|
||||||
val indexAction = clang_IndexAction_create(index)
|
|
||||||
val callbacks = nativeHeap.alloc<IndexerCallbacks>()
|
|
||||||
|
|
||||||
val res = NativeIndexImpl()
|
|
||||||
val nativeIndexPtr = StableObjPtr.create(res)
|
|
||||||
val clientData = nativeIndexPtr.value
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
with(callbacks) {
|
val translationUnit = library.parse(index).ensureNoCompileErrors()
|
||||||
abortQuery.value = null
|
try {
|
||||||
diagnostic.value = null
|
memScoped {
|
||||||
enteredMainFile.value = null
|
val callbacks = alloc<IndexerCallbacks>()
|
||||||
ppIncludedFile.value = null
|
|
||||||
importedASTFile.value = null
|
val nativeIndexPtr = StableObjPtr.create(nativeIndex)
|
||||||
startedTranslationUnit.value = null
|
val clientData = nativeIndexPtr.value
|
||||||
indexDeclaration.value = staticCFunction { clientData, info ->
|
|
||||||
val nativeIndex = StableObjPtr.fromValue(clientData!!).get() as NativeIndexImpl
|
try {
|
||||||
nativeIndex.indexDeclaration(info!!.pointed)
|
with(callbacks) {
|
||||||
|
abortQuery.value = null
|
||||||
|
diagnostic.value = null
|
||||||
|
enteredMainFile.value = null
|
||||||
|
ppIncludedFile.value = null
|
||||||
|
importedASTFile.value = null
|
||||||
|
startedTranslationUnit.value = null
|
||||||
|
indexDeclaration.value = staticCFunction { clientData, info ->
|
||||||
|
val nativeIndex = StableObjPtr.fromValue(clientData!!).get() as NativeIndexImpl
|
||||||
|
nativeIndex.indexDeclaration(info!!.pointed)
|
||||||
|
}
|
||||||
|
indexEntityReference.value = null
|
||||||
|
}
|
||||||
|
|
||||||
|
clang_indexTranslationUnit(indexAction, clientData,
|
||||||
|
callbacks.ptr, IndexerCallbacks.size.toInt(),
|
||||||
|
0, translationUnit)
|
||||||
|
|
||||||
|
} finally {
|
||||||
|
nativeIndexPtr.dispose()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
indexEntityReference.value = null
|
} finally {
|
||||||
|
clang_disposeTranslationUnit(translationUnit)
|
||||||
}
|
}
|
||||||
|
|
||||||
val commandLineArgs = nativeHeap.allocArrayOfPointersTo(*args1)[0].ptr
|
|
||||||
|
|
||||||
clang_indexSourceFile(indexAction, clientData, callbacks.ptr, IndexerCallbacks.size.toInt(),
|
|
||||||
0, headerFile.path, commandLineArgs, args1.size, null, 0, null, 0)
|
|
||||||
|
|
||||||
|
|
||||||
return res
|
|
||||||
|
|
||||||
} finally {
|
} finally {
|
||||||
nativeIndexPtr.dispose()
|
clang_IndexAction_dispose(indexAction)
|
||||||
|
clang_disposeIndex(index)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+234
@@ -0,0 +1,234 @@
|
|||||||
|
package org.jetbrains.kotlin.native.interop.indexer
|
||||||
|
|
||||||
|
import clang.*
|
||||||
|
import kotlinx.cinterop.memScoped
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds all "macro constants" and registers them as [NativeIndex.constants] in given index.
|
||||||
|
*/
|
||||||
|
internal fun findMacroConstants(library: NativeLibrary, nativeIndex: NativeIndexImpl) {
|
||||||
|
val names = collectMacroConstantsNames(library)
|
||||||
|
// TODO: apply user-defined filters.
|
||||||
|
|
||||||
|
val constants = expandMacroConstants(library, names, typeConverter = nativeIndex::convertType)
|
||||||
|
|
||||||
|
nativeIndex.macroConstants.addAll(constants)
|
||||||
|
}
|
||||||
|
|
||||||
|
private typealias TypeConverter = (CXType) -> Type
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For each name expands the macro with this name declared in the library,
|
||||||
|
* checking if it gets expanded to a constant expression.
|
||||||
|
*
|
||||||
|
* @return the list of constants.
|
||||||
|
*/
|
||||||
|
private fun expandMacroConstants(
|
||||||
|
originalLibrary: NativeLibrary,
|
||||||
|
names: List<String>,
|
||||||
|
typeConverter: TypeConverter
|
||||||
|
): List<ConstantDef> {
|
||||||
|
|
||||||
|
// Each macro is expanded by parsing it separately with the library;
|
||||||
|
// so precompile library headers to significantly speed up the parsing:
|
||||||
|
val library = originalLibrary.precompileHeaders()
|
||||||
|
|
||||||
|
val index = clang_createIndex(excludeDeclarationsFromPCH = 1, displayDiagnostics = 0)!!
|
||||||
|
try {
|
||||||
|
val sourceFile = library.createTempSource()
|
||||||
|
val translationUnit = parseTranslationUnit(index, sourceFile, library.compilerArgs, options = 0)
|
||||||
|
.ensureNoCompileErrors()
|
||||||
|
|
||||||
|
try {
|
||||||
|
return names.mapNotNull {
|
||||||
|
expandMacroAsConstant(library, translationUnit, sourceFile, it, typeConverter)
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
clang_disposeTranslationUnit(translationUnit)
|
||||||
|
}
|
||||||
|
|
||||||
|
} finally {
|
||||||
|
clang_disposeIndex(index)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Expands the macro [name] defined in [library].
|
||||||
|
* Returns the resulting constant or `null` if the result is not a constant (expression).
|
||||||
|
*
|
||||||
|
* As a side effect, modifies the [sourceFile] and reparses the [translationUnit].
|
||||||
|
*/
|
||||||
|
private fun expandMacroAsConstant(
|
||||||
|
library: NativeLibrary,
|
||||||
|
translationUnit: CXTranslationUnit,
|
||||||
|
sourceFile: File,
|
||||||
|
name: String,
|
||||||
|
typeConverter: TypeConverter
|
||||||
|
): ConstantDef? {
|
||||||
|
|
||||||
|
reparseWithCodeSnippet(library, translationUnit, sourceFile, name)
|
||||||
|
|
||||||
|
if (getCompileErrors(translationUnit).firstOrNull() == null) {
|
||||||
|
return processCodeSnippet(translationUnit, name, typeConverter)
|
||||||
|
} else {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds the code snippet to be then processed with [processCodeSnippet] to the [sourceFile]
|
||||||
|
* and reparses the [translationUnit].
|
||||||
|
*
|
||||||
|
* The code snippet should allow to extract the constant value using libclang API.
|
||||||
|
*/
|
||||||
|
private fun reparseWithCodeSnippet(library: NativeLibrary,
|
||||||
|
translationUnit: CXTranslationUnit, sourceFile: File,
|
||||||
|
name: String) {
|
||||||
|
|
||||||
|
// TODO: consider using CXUnsavedFile instead of writing the modified file to OS file system.
|
||||||
|
sourceFile.bufferedWriter().use { writer ->
|
||||||
|
writer.appendPreamble(library)
|
||||||
|
|
||||||
|
// Note: clang_Cursor_Evaluate permits expression to have side-effects,
|
||||||
|
// so the code pattern should force the constant evaluation that corresponds to language rules.
|
||||||
|
val codeSnippetLines = when (library.language) {
|
||||||
|
// Note: __auto_type is a GNU extension which is supported by clang.
|
||||||
|
Language.C -> listOf(
|
||||||
|
"const __auto_type KNI_INDEXER_VARIABLE = $name;",
|
||||||
|
// Clang evaluate API doesn't provide a way to get a `long long` value yet;
|
||||||
|
// so extract such values from the enum declaration:
|
||||||
|
"enum { KNI_INDEXER_ENUM_CONST = (long long)KNI_INDEXER_VARIABLE };"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
codeSnippetLines.forEach { writer.append(it) }
|
||||||
|
}
|
||||||
|
clang_reparseTranslationUnit(translationUnit, 0, null, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks that [translationUnit] is parsed exactly as expected for the code appended by [reparseWithCodeSnippet],
|
||||||
|
* and returns the constant on success.
|
||||||
|
*/
|
||||||
|
private fun processCodeSnippet(
|
||||||
|
translationUnit: CXTranslationUnit,
|
||||||
|
name: String,
|
||||||
|
typeConverter: TypeConverter
|
||||||
|
): ConstantDef? {
|
||||||
|
|
||||||
|
var state = VisitorState.EXPECT_VARIABLE
|
||||||
|
var evalResultKind: CXEvalResultKind? = null
|
||||||
|
var type: Type? = null
|
||||||
|
var longValue: Long? = null
|
||||||
|
var doubleValue: Double? = null
|
||||||
|
|
||||||
|
val visitor: CursorVisitor = { cursor: CXCursor, parent: CXCursor ->
|
||||||
|
val kind = cursor.kind.value
|
||||||
|
when {
|
||||||
|
state == VisitorState.EXPECT_VARIABLE && kind == CXCursorKind.CXCursor_VarDecl -> {
|
||||||
|
state = VisitorState.EXPECT_VARIABLE_VALUE
|
||||||
|
val evalResult = clang_Cursor_Evaluate(cursor)
|
||||||
|
if (evalResult != null) {
|
||||||
|
try {
|
||||||
|
evalResultKind = clang_EvalResult_getKind(evalResult)
|
||||||
|
if (evalResultKind == CXEvalResultKind.CXEval_Float) {
|
||||||
|
doubleValue = clang_EvalResult_getAsDouble(evalResult)
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
clang_EvalResult_dispose(evalResult)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
CXChildVisitResult.CXChildVisit_Recurse
|
||||||
|
}
|
||||||
|
|
||||||
|
state == VisitorState.EXPECT_VARIABLE_VALUE && clang_isExpression(kind) != 0 -> {
|
||||||
|
state = VisitorState.EXPECT_ENUM
|
||||||
|
type = memScoped {
|
||||||
|
val cursorType = clang_getCursorType(cursor, memScope)
|
||||||
|
typeConverter(cursorType)
|
||||||
|
}
|
||||||
|
CXChildVisitResult.CXChildVisit_Continue
|
||||||
|
}
|
||||||
|
|
||||||
|
state == VisitorState.EXPECT_ENUM && kind == CXCursorKind.CXCursor_EnumDecl -> {
|
||||||
|
state = VisitorState.EXPECT_ENUM_CONST
|
||||||
|
CXChildVisitResult.CXChildVisit_Recurse
|
||||||
|
}
|
||||||
|
|
||||||
|
state == VisitorState.EXPECT_ENUM_CONST && kind == CXCursorKind.CXCursor_EnumConstantDecl -> {
|
||||||
|
state = VisitorState.EXPECT_ENUM_CONST_VALUE
|
||||||
|
if (evalResultKind == CXEvalResultKind.CXEval_Int) {
|
||||||
|
longValue = clang_getEnumConstantDeclValue(cursor)
|
||||||
|
}
|
||||||
|
CXChildVisitResult.CXChildVisit_Recurse
|
||||||
|
}
|
||||||
|
|
||||||
|
state == VisitorState.EXPECT_ENUM_CONST_VALUE && clang_isExpression(kind) != 0 -> {
|
||||||
|
state = VisitorState.EXPECT_END
|
||||||
|
CXChildVisitResult.CXChildVisit_Continue
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> {
|
||||||
|
state = VisitorState.INVALID
|
||||||
|
CXChildVisitResult.CXChildVisit_Break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
visitChildren(translationUnit, visitor)
|
||||||
|
|
||||||
|
if (state != VisitorState.EXPECT_END) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
return when (evalResultKind!!) {
|
||||||
|
CXEvalResultKind.CXEval_Int -> IntegerConstantDef(name, type!!, longValue!!)
|
||||||
|
CXEvalResultKind.CXEval_Float -> FloatingConstantDef(name, type!!, doubleValue!!)
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class VisitorState {
|
||||||
|
EXPECT_VARIABLE, EXPECT_VARIABLE_VALUE,
|
||||||
|
EXPECT_ENUM, EXPECT_ENUM_CONST, EXPECT_ENUM_CONST_VALUE,
|
||||||
|
EXPECT_END, INVALID
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun collectMacroConstantsNames(library: NativeLibrary): List<String> {
|
||||||
|
val result = mutableSetOf<String>()
|
||||||
|
val index = clang_createIndex(excludeDeclarationsFromPCH = 0, displayDiagnostics = 0)!!
|
||||||
|
try {
|
||||||
|
// Include macros into AST:
|
||||||
|
val options = CXTranslationUnit_Flags.CXTranslationUnit_DetailedPreprocessingRecord.value
|
||||||
|
|
||||||
|
val translationUnit = library.parse(index, options).ensureNoCompileErrors()
|
||||||
|
try {
|
||||||
|
visitChildren(translationUnit) { cursor, parent ->
|
||||||
|
if (cursor.kind.value == CXCursorKind.CXCursor_MacroDefinition && canMacroBeConstant(cursor)) {
|
||||||
|
val spelling = getCursorSpelling(cursor)
|
||||||
|
result.add(spelling)
|
||||||
|
}
|
||||||
|
CXChildVisitResult.CXChildVisit_Continue
|
||||||
|
}
|
||||||
|
|
||||||
|
} finally {
|
||||||
|
clang_disposeTranslationUnit(translationUnit)
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
clang_disposeIndex(index)
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.toList()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun canMacroBeConstant(cursor: CXCursor): Boolean {
|
||||||
|
|
||||||
|
if (clang_Cursor_isMacroFunctionLike(cursor) != 0) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: check number of tokens and filter out empty definitions;
|
||||||
|
// Requires updating to 3.9.1 due to https://bugs.llvm.org//show_bug.cgi?id=9069
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
+14
-2
@@ -1,11 +1,15 @@
|
|||||||
package org.jetbrains.kotlin.native.interop.indexer
|
package org.jetbrains.kotlin.native.interop.indexer
|
||||||
|
|
||||||
import java.io.File
|
enum class Language {
|
||||||
|
C
|
||||||
|
}
|
||||||
|
|
||||||
|
class NativeLibrary(val includes: List<String>, val compilerArgs: List<String>, val language: Language)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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(headerFile: File, args: List<String>): NativeIndex = buildNativeIndexImpl(headerFile, args)
|
fun buildNativeIndex(library: NativeLibrary): NativeIndex = buildNativeIndexImpl(library)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class describes the IR of definitions from C header file(s).
|
* This class describes the IR of definitions from C header file(s).
|
||||||
@@ -15,6 +19,7 @@ abstract class NativeIndex {
|
|||||||
abstract val enums: List<EnumDef>
|
abstract val enums: List<EnumDef>
|
||||||
abstract val typedefs: List<TypedefDef>
|
abstract val typedefs: List<TypedefDef>
|
||||||
abstract val functions: List<FunctionDecl>
|
abstract val functions: List<FunctionDecl>
|
||||||
|
abstract val macroConstants: List<ConstantDef>
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -75,6 +80,10 @@ class FunctionDecl(val name: String, val parameters: List<Parameter>, val return
|
|||||||
*/
|
*/
|
||||||
class TypedefDef(val aliased: Type, val name: String)
|
class TypedefDef(val aliased: Type, val name: String)
|
||||||
|
|
||||||
|
abstract class ConstantDef(val name: String, val type: Type)
|
||||||
|
class IntegerConstantDef(name: String, type: Type, val value: Long) : ConstantDef(name, type)
|
||||||
|
class FloatingConstantDef(name: String, type: Type, val value: Double) : ConstantDef(name, type)
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* C type.
|
* C type.
|
||||||
@@ -100,6 +109,9 @@ object UIntPtrType : PrimitiveType
|
|||||||
object Int64Type : PrimitiveType
|
object Int64Type : PrimitiveType
|
||||||
object UInt64Type : PrimitiveType
|
object UInt64Type : PrimitiveType
|
||||||
|
|
||||||
|
object Float32Type : PrimitiveType
|
||||||
|
object Float64Type : PrimitiveType
|
||||||
|
|
||||||
data class RecordType(val decl: StructDecl) : Type
|
data class RecordType(val decl: StructDecl) : Type
|
||||||
|
|
||||||
data class EnumType(val def: EnumDef) : Type
|
data class EnumType(val def: EnumDef) : Type
|
||||||
|
|||||||
@@ -0,0 +1,160 @@
|
|||||||
|
package org.jetbrains.kotlin.native.interop.indexer
|
||||||
|
|
||||||
|
import clang.*
|
||||||
|
import kotlinx.cinterop.*
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
internal fun CXString.convertAndDispose(): String {
|
||||||
|
try {
|
||||||
|
return clang_getCString(this)!!.asCString().toString()
|
||||||
|
} finally {
|
||||||
|
clang_disposeString(this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun getCursorSpelling(cursor: CXCursor) = memScoped {
|
||||||
|
clang_getCursorSpelling(cursor, memScope).convertAndDispose()
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun convertUnqualifiedPrimitiveType(type: CXType): Type = when (type.kind.value) {
|
||||||
|
// TODO: is e.g. CXType_Int guaranteed to be int32_t?
|
||||||
|
|
||||||
|
CXTypeKind.CXType_Char_U, CXTypeKind.CXType_UChar -> UInt8Type
|
||||||
|
CXTypeKind.CXType_Char_S, CXTypeKind.CXType_SChar -> Int8Type
|
||||||
|
|
||||||
|
CXTypeKind.CXType_UShort -> UInt16Type
|
||||||
|
CXTypeKind.CXType_Short -> Int16Type
|
||||||
|
|
||||||
|
CXTypeKind.CXType_UInt -> UInt32Type
|
||||||
|
CXTypeKind.CXType_Int -> Int32Type
|
||||||
|
|
||||||
|
CXTypeKind.CXType_ULong -> UIntPtrType
|
||||||
|
CXTypeKind.CXType_Long -> IntPtrType
|
||||||
|
|
||||||
|
CXTypeKind.CXType_ULongLong -> UInt64Type
|
||||||
|
CXTypeKind.CXType_LongLong -> Int64Type
|
||||||
|
|
||||||
|
CXTypeKind.CXType_Float -> Float32Type
|
||||||
|
CXTypeKind.CXType_Double -> Float64Type
|
||||||
|
|
||||||
|
else -> UnsupportedType
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun parseTranslationUnit(
|
||||||
|
index: CXIndex,
|
||||||
|
sourceFile: File,
|
||||||
|
compilerArgs: List<String>,
|
||||||
|
options: Int
|
||||||
|
): CXTranslationUnit {
|
||||||
|
memScoped {
|
||||||
|
val result = clang_parseTranslationUnit(
|
||||||
|
index,
|
||||||
|
sourceFile.absolutePath,
|
||||||
|
compilerArgs.toNativeStringArray(memScope)[0].ptr, compilerArgs.size,
|
||||||
|
null, 0,
|
||||||
|
options
|
||||||
|
)!!
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun NativeLibrary.parse(index: CXIndex, options: Int = 0): CXTranslationUnit =
|
||||||
|
parseTranslationUnit(index, this.createTempSource(), this.compilerArgs, options)
|
||||||
|
|
||||||
|
internal fun getCompileErrors(translationUnit: CXTranslationUnit): Sequence<String> {
|
||||||
|
val numDiagnostics = clang_getNumDiagnostics(translationUnit)
|
||||||
|
return (0 .. numDiagnostics - 1).asSequence()
|
||||||
|
.map { index -> clang_getDiagnostic(translationUnit, index)!! }
|
||||||
|
.filter {
|
||||||
|
val severity = clang_getDiagnosticSeverity(it)
|
||||||
|
severity == CXDiagnosticSeverity.CXDiagnostic_Error ||
|
||||||
|
severity == CXDiagnosticSeverity.CXDiagnostic_Fatal
|
||||||
|
}.map {
|
||||||
|
memScoped {
|
||||||
|
clang_formatDiagnostic(it, clang_defaultDiagnosticDisplayOptions(), memScope).convertAndDispose()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun CXTranslationUnit.ensureNoCompileErrors(): CXTranslationUnit {
|
||||||
|
val firstError = getCompileErrors(this).firstOrNull() ?: return this
|
||||||
|
throw Error(firstError)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal typealias CursorVisitor = (cursor: CXCursor, parent: CXCursor) -> CXChildVisitResult
|
||||||
|
|
||||||
|
internal fun visitChildren(parent: CXCursor, visitor: CursorVisitor) {
|
||||||
|
val visitorPtr = StableObjPtr.create(visitor)
|
||||||
|
val clientData = visitorPtr.value
|
||||||
|
clang_visitChildren(parent, staticCFunction { cursor, parent, clientData ->
|
||||||
|
val visitor = StableObjPtr.fromValue(clientData!!).get() as CursorVisitor
|
||||||
|
visitor(cursor, parent)
|
||||||
|
}, clientData)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun visitChildren(translationUnit: CXTranslationUnit, visitor: CursorVisitor) {
|
||||||
|
memScoped {
|
||||||
|
visitChildren(clang_getTranslationUnitCursor(translationUnit, memScope), visitor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun List<String>.toNativeStringArray(placement: NativePlacement): CArray<CPointerVar<CInt8Var>> {
|
||||||
|
return placement.allocArray(this.size) { index ->
|
||||||
|
this.value = this@toNativeStringArray[index].toCString(placement)!!.asCharPtr()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal val NativeLibrary.preambleLines: List<String>
|
||||||
|
get() = this.includes.map { "#include <$it>" }
|
||||||
|
|
||||||
|
internal fun Appendable.appendPreamble(library: NativeLibrary) = this.apply {
|
||||||
|
library.preambleLines.forEach {
|
||||||
|
this.appendln(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates temporary source file which includes the library.
|
||||||
|
*/
|
||||||
|
internal fun NativeLibrary.createTempSource(): File {
|
||||||
|
val suffix = when (language) {
|
||||||
|
Language.C -> ".c"
|
||||||
|
}
|
||||||
|
val result = createTempFile(suffix = suffix)
|
||||||
|
result.deleteOnExit()
|
||||||
|
|
||||||
|
result.bufferedWriter().use { writer ->
|
||||||
|
writer.appendPreamble(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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() }
|
||||||
|
|
||||||
|
val index = clang_createIndex(excludeDeclarationsFromPCH = 0, displayDiagnostics = 0)!!
|
||||||
|
try {
|
||||||
|
val options = CXTranslationUnit_Flags.CXTranslationUnit_ForSerialization.value
|
||||||
|
val translationUnit = this.parse(index, options).ensureNoCompileErrors()
|
||||||
|
try {
|
||||||
|
clang_saveTranslationUnit(translationUnit, precompiledHeader.absolutePath, 0)
|
||||||
|
} finally {
|
||||||
|
clang_disposeTranslationUnit(translationUnit)
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
clang_disposeIndex(index)
|
||||||
|
}
|
||||||
|
|
||||||
|
return NativeLibrary(
|
||||||
|
includes = emptyList(),
|
||||||
|
compilerArgs = this.compilerArgs + listOf("-include-pch", precompiledHeader.absolutePath),
|
||||||
|
language = this.language
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -177,7 +177,7 @@ class CString private constructor(override val rawPtr: NativePtr) : CPointed {
|
|||||||
return decodeFromUtf8(bytes) // TODO: encoding
|
return decodeFromUtf8(bytes) // TODO: encoding
|
||||||
}
|
}
|
||||||
|
|
||||||
fun asCharPtr() = reinterpret<CInt8Var>()
|
fun asCharPtr() = reinterpret<CInt8Var>().ptr
|
||||||
}
|
}
|
||||||
|
|
||||||
fun CString.Companion.fromString(str: String?, placement: NativePlacement): CString? {
|
fun CString.Companion.fromString(str: String?, placement: NativePlacement): CString? {
|
||||||
|
|||||||
+78
-2
@@ -96,6 +96,8 @@ class StubGenerator(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val macroConstantsByName = nativeIndex.macroConstants.associateBy { it.name }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The output currently used by the generator.
|
* The output currently used by the generator.
|
||||||
* Should append line separator after any usage.
|
* Should append line separator after any usage.
|
||||||
@@ -155,6 +157,8 @@ class StubGenerator(
|
|||||||
is UIntPtrType -> "uintptr_t"
|
is UIntPtrType -> "uintptr_t"
|
||||||
is Int64Type -> "int64_t"
|
is Int64Type -> "int64_t"
|
||||||
is UInt64Type -> "uint64_t"
|
is UInt64Type -> "uint64_t"
|
||||||
|
is Float32Type -> "float"
|
||||||
|
is Float64Type -> "double"
|
||||||
|
|
||||||
is PointerType -> {
|
is PointerType -> {
|
||||||
val pointeeType = this.pointeeType
|
val pointeeType = this.pointeeType
|
||||||
@@ -190,6 +194,8 @@ class StubGenerator(
|
|||||||
is Int32Type, is UInt32Type -> "Int"
|
is Int32Type, is UInt32Type -> "Int"
|
||||||
is IntPtrType, is UIntPtrType, // TODO: 64-bit specific
|
is IntPtrType, is UIntPtrType, // TODO: 64-bit specific
|
||||||
is Int64Type, is UInt64Type -> "Long"
|
is Int64Type, is UInt64Type -> "Long"
|
||||||
|
is Float32Type -> "Float"
|
||||||
|
is Float64Type -> "Double"
|
||||||
|
|
||||||
else -> throw NotImplementedError()
|
else -> throw NotImplementedError()
|
||||||
}
|
}
|
||||||
@@ -300,6 +306,8 @@ class StubGenerator(
|
|||||||
is Int32Type, is UInt32Type -> "CInt32Var"
|
is Int32Type, is UInt32Type -> "CInt32Var"
|
||||||
is IntPtrType, is UIntPtrType, // TODO: 64-bit specific
|
is IntPtrType, is UIntPtrType, // TODO: 64-bit specific
|
||||||
is Int64Type, is UInt64Type -> "CInt64Var"
|
is Int64Type, is UInt64Type -> "CInt64Var"
|
||||||
|
is Float32Type -> "CFloat32Var"
|
||||||
|
is Float64Type -> "CFloat64Var"
|
||||||
else -> TODO(type.toString())
|
else -> TODO(type.toString())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -551,8 +559,13 @@ class StubGenerator(
|
|||||||
}
|
}
|
||||||
|
|
||||||
out("// ${e.spelling}")
|
out("// ${e.spelling}")
|
||||||
e.values.forEach {
|
for (value in e.values) {
|
||||||
out("val ${it.name}: ${e.baseType.kotlinType} = ${it.value}")
|
if (value.name in macroConstantsByName) {
|
||||||
|
// Macro "overrides" the original enum constant.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
out("val ${value.name}: ${e.baseType.kotlinType} = ${value.value}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -757,6 +770,64 @@ class StubGenerator(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun integerLiteral(type: Type, value: Long): String? {
|
||||||
|
if (value == Long.MIN_VALUE) {
|
||||||
|
return "${value + 1} - 1" // Workaround for "The value is out of range" compile error.
|
||||||
|
}
|
||||||
|
|
||||||
|
val unwrappedType = type.unwrapTypedefs()
|
||||||
|
if (unwrappedType !is PrimitiveType) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
val narrowedValue: Number = when (unwrappedType.kotlinType) {
|
||||||
|
"Byte" -> value.toByte()
|
||||||
|
"Short" -> value.toShort()
|
||||||
|
"Int" -> value.toInt()
|
||||||
|
"Long" -> value
|
||||||
|
else -> return null
|
||||||
|
}
|
||||||
|
|
||||||
|
return narrowedValue.toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun floatingLiteral(type: Type, value: Double): String? {
|
||||||
|
return when (type.unwrapTypedefs()) {
|
||||||
|
Float32Type -> {
|
||||||
|
val floatValue = value.toFloat()
|
||||||
|
val bits = java.lang.Float.floatToRawIntBits(floatValue)
|
||||||
|
"bitsToFloat($bits) /* == $floatValue */"
|
||||||
|
}
|
||||||
|
Float64Type -> {
|
||||||
|
val bits = java.lang.Double.doubleToRawLongBits(value)
|
||||||
|
"bitsToDouble($bits) /* == $value */"
|
||||||
|
}
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun generateConstant(constant: ConstantDef) {
|
||||||
|
val literal = when (constant) {
|
||||||
|
is IntegerConstantDef -> integerLiteral(constant.type, constant.value) ?: return
|
||||||
|
is FloatingConstantDef -> floatingLiteral(constant.type, constant.value) ?: return
|
||||||
|
else -> {
|
||||||
|
// Not supported yet, ignore:
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val kotlinType = mirror(constant.type).argType
|
||||||
|
|
||||||
|
// TODO: improve value rendering.
|
||||||
|
|
||||||
|
// TODO: consider using `const` modifier.
|
||||||
|
// It is not currently possible for floating literals.
|
||||||
|
// Also it provokes constant propagation which can reduce binary compatibility
|
||||||
|
// when replacing interop stubs without recompiling the application.
|
||||||
|
|
||||||
|
out("val ${constant.name}: $kotlinType = $literal")
|
||||||
|
}
|
||||||
|
|
||||||
private val FunctionDecl.stubSymbolName: String
|
private val FunctionDecl.stubSymbolName: String
|
||||||
get() {
|
get() {
|
||||||
require(platform == KotlinPlatform.NATIVE)
|
require(platform == KotlinPlatform.NATIVE)
|
||||||
@@ -803,6 +874,11 @@ class StubGenerator(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nativeIndex.macroConstants.forEach {
|
||||||
|
generateConstant(it)
|
||||||
|
}
|
||||||
|
out("")
|
||||||
|
|
||||||
nativeIndex.structs.forEach { s ->
|
nativeIndex.structs.forEach { s ->
|
||||||
try {
|
try {
|
||||||
transaction {
|
transaction {
|
||||||
|
|||||||
+5
-16
@@ -1,6 +1,8 @@
|
|||||||
package org.jetbrains.kotlin.native.interop.gen.jvm
|
package org.jetbrains.kotlin.native.interop.gen.jvm
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.native.interop.indexer.Language
|
||||||
import org.jetbrains.kotlin.native.interop.indexer.NativeIndex
|
import org.jetbrains.kotlin.native.interop.indexer.NativeIndex
|
||||||
|
import org.jetbrains.kotlin.native.interop.indexer.NativeLibrary
|
||||||
import org.jetbrains.kotlin.native.interop.indexer.buildNativeIndex
|
import org.jetbrains.kotlin.native.interop.indexer.buildNativeIndex
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.lang.IllegalArgumentException
|
import java.lang.IllegalArgumentException
|
||||||
@@ -118,7 +120,8 @@ private fun processLib(ktGenRoot: String,
|
|||||||
|
|
||||||
val headerFiles = config.getSpaceSeparated("headers") + additionalHeaders
|
val headerFiles = config.getSpaceSeparated("headers") + additionalHeaders
|
||||||
val compilerOpts = config.getSpaceSeparated("compilerOpts") + additionalCompilerOpts
|
val compilerOpts = config.getSpaceSeparated("compilerOpts") + additionalCompilerOpts
|
||||||
val compiler = config.getProperty("compiler") ?: "clang"
|
val compiler = "clang"
|
||||||
|
val language = Language.C
|
||||||
val linkerOpts = config.getSpaceSeparated("linkerOpts").toTypedArray() + additionalLinkerOpts
|
val linkerOpts = config.getSpaceSeparated("linkerOpts").toTypedArray() + additionalLinkerOpts
|
||||||
val linker = args["-linker"]?.singleOrNull() ?: config.getProperty("linker") ?: "clang"
|
val linker = args["-linker"]?.singleOrNull() ?: config.getProperty("linker") ?: "clang"
|
||||||
val excludedFunctions = config.getSpaceSeparated("excludedFunctions").toSet()
|
val excludedFunctions = config.getSpaceSeparated("excludedFunctions").toSet()
|
||||||
@@ -135,7 +138,7 @@ private fun processLib(ktGenRoot: String,
|
|||||||
|
|
||||||
val libName = fqParts.joinToString("") + "stubs"
|
val libName = fqParts.joinToString("") + "stubs"
|
||||||
|
|
||||||
val nativeIndex = buildNativeIndex(headerFiles, compilerOpts)
|
val nativeIndex = buildNativeIndex(NativeLibrary(headerFiles, compilerOpts, language))
|
||||||
|
|
||||||
val gen = StubGenerator(nativeIndex, outKtPkg, libName, excludedFunctions, generateShims, platform)
|
val gen = StubGenerator(nativeIndex, outKtPkg, libName, excludedFunctions, generateShims, platform)
|
||||||
|
|
||||||
@@ -200,17 +203,3 @@ private fun processLib(ktGenRoot: String,
|
|||||||
|
|
||||||
outCFile.delete()
|
outCFile.delete()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun buildNativeIndex(headerFiles: List<String>, compilerOpts: List<String>): NativeIndex {
|
|
||||||
val tempHeaderFile = createTempFile(suffix = ".h")
|
|
||||||
tempHeaderFile.deleteOnExit()
|
|
||||||
tempHeaderFile.writer().buffered().use { reader ->
|
|
||||||
headerFiles.forEach {
|
|
||||||
reader.appendln("#include <$it>")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val res = buildNativeIndex(tempHeaderFile, compilerOpts)
|
|
||||||
println(res)
|
|
||||||
return res
|
|
||||||
}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user