restricted c header imports
This commit is contained in:
@@ -0,0 +1,140 @@
|
||||
package cimport
|
||||
|
||||
import "../target"
|
||||
import "core:mem"
|
||||
|
||||
Type_Id :: distinct u32
|
||||
INVALID_TYPE :: Type_Id(0xffff_ffff)
|
||||
|
||||
Type_Kind :: enum u8 {
|
||||
Invalid,
|
||||
Void,
|
||||
C_Char,
|
||||
C_Schar,
|
||||
C_Uchar,
|
||||
C_Short,
|
||||
C_Ushort,
|
||||
C_Int,
|
||||
C_Uint,
|
||||
C_Long,
|
||||
C_Ulong,
|
||||
C_Longlong,
|
||||
C_Ulonglong,
|
||||
C_Float,
|
||||
C_Double,
|
||||
C_Longdouble,
|
||||
Pointer,
|
||||
Record,
|
||||
}
|
||||
|
||||
Type :: struct {
|
||||
kind: Type_Kind,
|
||||
child: Type_Id,
|
||||
record: u32,
|
||||
mutable: bool,
|
||||
}
|
||||
|
||||
Record :: struct {
|
||||
name: string,
|
||||
identity: string,
|
||||
}
|
||||
|
||||
Alias :: struct {
|
||||
name: string,
|
||||
type: Type_Id,
|
||||
reason: string,
|
||||
}
|
||||
|
||||
Function :: struct {
|
||||
name: string,
|
||||
params: []Type_Id,
|
||||
result: Type_Id,
|
||||
reason: string,
|
||||
}
|
||||
|
||||
Unsupported :: struct {
|
||||
name: string,
|
||||
reason: string,
|
||||
}
|
||||
|
||||
Result :: struct {
|
||||
types: [dynamic]Type,
|
||||
records: [dynamic]Record,
|
||||
aliases: [dynamic]Alias,
|
||||
functions: [dynamic]Function,
|
||||
unsupported: [dynamic]Unsupported,
|
||||
error_message: string,
|
||||
infrastructure: bool,
|
||||
available: bool,
|
||||
allocator: mem.Allocator,
|
||||
}
|
||||
|
||||
init_result :: proc(allocator := context.allocator) -> Result {
|
||||
result: Result
|
||||
result.allocator = allocator
|
||||
result.types.allocator = allocator
|
||||
result.records.allocator = allocator
|
||||
result.aliases.allocator = allocator
|
||||
result.functions.allocator = allocator
|
||||
result.unsupported.allocator = allocator
|
||||
return result
|
||||
}
|
||||
|
||||
destroy_result :: proc(result: ^Result) {
|
||||
for record in result.records {
|
||||
delete(record.name, result.allocator)
|
||||
delete(record.identity, result.allocator)
|
||||
}
|
||||
for alias in result.aliases {
|
||||
delete(alias.name, result.allocator)
|
||||
delete(alias.reason, result.allocator)
|
||||
}
|
||||
for function in result.functions {
|
||||
delete(function.name, result.allocator)
|
||||
delete(function.params, result.allocator)
|
||||
delete(function.reason, result.allocator)
|
||||
}
|
||||
for item in result.unsupported {
|
||||
delete(item.name, result.allocator)
|
||||
delete(item.reason, result.allocator)
|
||||
}
|
||||
delete(result.error_message, result.allocator)
|
||||
delete(result.types)
|
||||
delete(result.records)
|
||||
delete(result.aliases)
|
||||
delete(result.functions)
|
||||
delete(result.unsupported)
|
||||
}
|
||||
|
||||
Request :: struct {
|
||||
path: string,
|
||||
include_paths: []string,
|
||||
defines: []string,
|
||||
target: target.Target,
|
||||
}
|
||||
|
||||
Backend_Proc :: proc(user_data: rawptr, request: Request, allocator: mem.Allocator) -> Result
|
||||
|
||||
Backend :: struct {
|
||||
import_header: Backend_Proc,
|
||||
user_data: rawptr,
|
||||
}
|
||||
|
||||
Options :: struct {
|
||||
include_paths: []string,
|
||||
defines: []string,
|
||||
backend: Backend,
|
||||
}
|
||||
|
||||
import_header :: proc(options: Options, path: string, selected := target.DEFAULT, allocator := context.allocator) -> Result {
|
||||
request := Request{
|
||||
path=path,
|
||||
include_paths=options.include_paths,
|
||||
defines=options.defines,
|
||||
target=selected,
|
||||
}
|
||||
if options.backend.import_header != nil {
|
||||
return options.backend.import_header(options.backend.user_data, request, allocator)
|
||||
}
|
||||
return import_with_libclang(nil, request, allocator)
|
||||
}
|
||||
@@ -0,0 +1,464 @@
|
||||
package cimport
|
||||
|
||||
import "../target"
|
||||
import "base:runtime"
|
||||
import "core:dynlib"
|
||||
import "core:fmt"
|
||||
import "core:mem"
|
||||
import "core:os/os2"
|
||||
import "core:strings"
|
||||
|
||||
CXCursor :: struct {
|
||||
kind: i32,
|
||||
xdata: i32,
|
||||
data: [3]rawptr,
|
||||
}
|
||||
|
||||
CXType :: struct {
|
||||
kind: i32,
|
||||
data: [2]rawptr,
|
||||
}
|
||||
|
||||
CXString :: struct {
|
||||
data: rawptr,
|
||||
private_flags: u32,
|
||||
}
|
||||
|
||||
CXIndex :: distinct rawptr
|
||||
CXTranslationUnit :: distinct rawptr
|
||||
CXDiagnostic :: distinct rawptr
|
||||
|
||||
Cursor_Visitor :: proc "c"(cursor, parent: CXCursor, client_data: rawptr) -> i32
|
||||
|
||||
Api :: struct {
|
||||
library: dynlib.Library,
|
||||
|
||||
create_index: proc "c"(i32, i32) -> CXIndex,
|
||||
dispose_index: proc "c"(CXIndex),
|
||||
parse_translation_unit: proc "c"(CXIndex, cstring, [^]cstring, i32, rawptr, u32, u32, ^CXTranslationUnit) -> i32,
|
||||
dispose_translation_unit: proc "c"(CXTranslationUnit),
|
||||
get_translation_unit_cursor: proc "c"(CXTranslationUnit) -> CXCursor,
|
||||
visit_children: proc "c"(CXCursor, Cursor_Visitor, rawptr) -> u32,
|
||||
get_cursor_kind: proc "c"(CXCursor) -> i32,
|
||||
get_cursor_spelling: proc "c"(CXCursor) -> CXString,
|
||||
get_cursor_usr: proc "c"(CXCursor) -> CXString,
|
||||
get_cursor_linkage: proc "c"(CXCursor) -> i32,
|
||||
get_cursor_type: proc "c"(CXCursor) -> CXType,
|
||||
get_typedef_underlying_type: proc "c"(CXCursor) -> CXType,
|
||||
get_type_declaration: proc "c"(CXType) -> CXCursor,
|
||||
get_canonical_type: proc "c"(CXType) -> CXType,
|
||||
get_pointee_type: proc "c"(CXType) -> CXType,
|
||||
get_result_type: proc "c"(CXType) -> CXType,
|
||||
get_num_arg_types: proc "c"(CXType) -> i32,
|
||||
get_arg_type: proc "c"(CXType, u32) -> CXType,
|
||||
is_const_qualified_type: proc "c"(CXType) -> u32,
|
||||
is_volatile_qualified_type: proc "c"(CXType) -> u32,
|
||||
cursor_is_variadic: proc "c"(CXCursor) -> u32,
|
||||
get_num_diagnostics: proc "c"(CXTranslationUnit) -> u32,
|
||||
get_diagnostic: proc "c"(CXTranslationUnit, u32) -> CXDiagnostic,
|
||||
get_diagnostic_severity: proc "c"(CXDiagnostic) -> i32,
|
||||
get_diagnostic_spelling: proc "c"(CXDiagnostic) -> CXString,
|
||||
dispose_diagnostic: proc "c"(CXDiagnostic),
|
||||
get_cstring: proc "c"(CXString) -> cstring,
|
||||
dispose_string: proc "c"(CXString),
|
||||
}
|
||||
|
||||
CXCursor_StructDecl :: i32(2)
|
||||
CXCursor_UnionDecl :: i32(3)
|
||||
CXCursor_EnumDecl :: i32(5)
|
||||
CXCursor_FunctionDecl :: i32(8)
|
||||
CXCursor_VarDecl :: i32(9)
|
||||
CXCursor_TypedefDecl :: i32(20)
|
||||
CXCursor_MacroDefinition :: i32(501)
|
||||
|
||||
CXLinkage_External :: i32(4)
|
||||
|
||||
CXType_Invalid :: i32(0)
|
||||
CXType_Unexposed :: i32(1)
|
||||
CXType_Void :: i32(2)
|
||||
CXType_Char_U :: i32(4)
|
||||
CXType_UChar :: i32(5)
|
||||
CXType_UShort :: i32(8)
|
||||
CXType_UInt :: i32(9)
|
||||
CXType_ULong :: i32(10)
|
||||
CXType_ULongLong :: i32(11)
|
||||
CXType_Char_S :: i32(13)
|
||||
CXType_SChar :: i32(14)
|
||||
CXType_Short :: i32(16)
|
||||
CXType_Int :: i32(17)
|
||||
CXType_Long :: i32(18)
|
||||
CXType_LongLong :: i32(19)
|
||||
CXType_Float :: i32(21)
|
||||
CXType_Double :: i32(22)
|
||||
CXType_LongDouble :: i32(23)
|
||||
CXType_Pointer :: i32(101)
|
||||
CXType_Record :: i32(105)
|
||||
CXType_Enum :: i32(106)
|
||||
CXType_Typedef :: i32(107)
|
||||
CXType_FunctionNoProto :: i32(110)
|
||||
CXType_FunctionProto :: i32(111)
|
||||
CXType_Elaborated :: i32(119)
|
||||
CXType_Attributed :: i32(163)
|
||||
|
||||
CXChildVisit_Continue :: i32(1)
|
||||
|
||||
CXTranslationUnit_DetailedPreprocessingRecord :: u32(0x01)
|
||||
CXTranslationUnit_SkipFunctionBodies :: u32(0x40)
|
||||
CXTranslationUnit_KeepGoing :: u32(0x200)
|
||||
|
||||
CXDiagnostic_Error :: i32(3)
|
||||
|
||||
load_proc :: proc(api: ^Api, name: string, destination: ^$T) -> bool {
|
||||
address, found := dynlib.symbol_address(api.library, name)
|
||||
if !found {
|
||||
return false
|
||||
}
|
||||
destination^ = transmute(T)address
|
||||
return true
|
||||
}
|
||||
|
||||
load_api_from :: proc(path: string) -> (Api, bool) {
|
||||
api: Api
|
||||
library, loaded := dynlib.load_library(path)
|
||||
if !loaded {
|
||||
return {}, false
|
||||
}
|
||||
api.library = library
|
||||
ok :=
|
||||
load_proc(&api, "clang_createIndex", &api.create_index) &&
|
||||
load_proc(&api, "clang_disposeIndex", &api.dispose_index) &&
|
||||
load_proc(&api, "clang_parseTranslationUnit2", &api.parse_translation_unit) &&
|
||||
load_proc(&api, "clang_disposeTranslationUnit", &api.dispose_translation_unit) &&
|
||||
load_proc(&api, "clang_getTranslationUnitCursor", &api.get_translation_unit_cursor) &&
|
||||
load_proc(&api, "clang_visitChildren", &api.visit_children) &&
|
||||
load_proc(&api, "clang_getCursorKind", &api.get_cursor_kind) &&
|
||||
load_proc(&api, "clang_getCursorSpelling", &api.get_cursor_spelling) &&
|
||||
load_proc(&api, "clang_getCursorUSR", &api.get_cursor_usr) &&
|
||||
load_proc(&api, "clang_getCursorLinkage", &api.get_cursor_linkage) &&
|
||||
load_proc(&api, "clang_getCursorType", &api.get_cursor_type) &&
|
||||
load_proc(&api, "clang_getTypedefDeclUnderlyingType", &api.get_typedef_underlying_type) &&
|
||||
load_proc(&api, "clang_getTypeDeclaration", &api.get_type_declaration) &&
|
||||
load_proc(&api, "clang_getCanonicalType", &api.get_canonical_type) &&
|
||||
load_proc(&api, "clang_getPointeeType", &api.get_pointee_type) &&
|
||||
load_proc(&api, "clang_getResultType", &api.get_result_type) &&
|
||||
load_proc(&api, "clang_getNumArgTypes", &api.get_num_arg_types) &&
|
||||
load_proc(&api, "clang_getArgType", &api.get_arg_type) &&
|
||||
load_proc(&api, "clang_isConstQualifiedType", &api.is_const_qualified_type) &&
|
||||
load_proc(&api, "clang_isVolatileQualifiedType", &api.is_volatile_qualified_type) &&
|
||||
load_proc(&api, "clang_Cursor_isVariadic", &api.cursor_is_variadic) &&
|
||||
load_proc(&api, "clang_getNumDiagnostics", &api.get_num_diagnostics) &&
|
||||
load_proc(&api, "clang_getDiagnostic", &api.get_diagnostic) &&
|
||||
load_proc(&api, "clang_getDiagnosticSeverity", &api.get_diagnostic_severity) &&
|
||||
load_proc(&api, "clang_getDiagnosticSpelling", &api.get_diagnostic_spelling) &&
|
||||
load_proc(&api, "clang_disposeDiagnostic", &api.dispose_diagnostic) &&
|
||||
load_proc(&api, "clang_getCString", &api.get_cstring) &&
|
||||
load_proc(&api, "clang_disposeString", &api.dispose_string)
|
||||
if !ok {
|
||||
_ = dynlib.unload_library(api.library)
|
||||
return {}, false
|
||||
}
|
||||
return api, true
|
||||
}
|
||||
|
||||
load_api :: proc() -> (Api, bool) {
|
||||
if override, found := os2.lookup_env_alloc("BROLANG_LIBCLANG_PATH", context.temp_allocator); found {
|
||||
if api, ok := load_api_from(override); ok {
|
||||
return api, true
|
||||
}
|
||||
}
|
||||
candidates := [?]string{
|
||||
"/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/libclang.dylib",
|
||||
"/Library/Developer/CommandLineTools/usr/lib/libclang.dylib",
|
||||
"/opt/homebrew/opt/llvm/lib/libclang.dylib",
|
||||
"/opt/homebrew/opt/llvm@21/lib/libclang.dylib",
|
||||
"libclang.dylib",
|
||||
"libclang.so",
|
||||
}
|
||||
for candidate in candidates {
|
||||
if api, ok := load_api_from(candidate); ok {
|
||||
return api, true
|
||||
}
|
||||
}
|
||||
return {}, false
|
||||
}
|
||||
|
||||
clone_cx_string :: proc(api: ^Api, value: CXString, allocator: mem.Allocator) -> string {
|
||||
defer api.dispose_string(value)
|
||||
text := api.get_cstring(value)
|
||||
if text == nil {
|
||||
return fmt.aprintf("", allocator=allocator)
|
||||
}
|
||||
return fmt.aprintf("%s", string(text), allocator=allocator)
|
||||
}
|
||||
|
||||
Context :: struct {
|
||||
api: ^Api,
|
||||
result: ^Result,
|
||||
allocator: mem.Allocator,
|
||||
}
|
||||
|
||||
add_type :: proc(ctx: ^Context, value: Type) -> Type_Id {
|
||||
id := Type_Id(len(ctx.result.types))
|
||||
append(&ctx.result.types, value)
|
||||
return id
|
||||
}
|
||||
|
||||
find_record :: proc(ctx: ^Context, identity: string) -> (u32, bool) {
|
||||
for record, index in ctx.result.records {
|
||||
if record.identity == identity {
|
||||
return u32(index), true
|
||||
}
|
||||
}
|
||||
return 0, false
|
||||
}
|
||||
|
||||
add_record :: proc(ctx: ^Context, declaration: CXCursor, preferred_name: string) -> u32 {
|
||||
identity := clone_cx_string(ctx.api, ctx.api.get_cursor_usr(declaration), ctx.allocator)
|
||||
if len(identity) == 0 {
|
||||
delete(identity, ctx.allocator)
|
||||
identity = clone_cx_string(ctx.api, ctx.api.get_cursor_spelling(declaration), ctx.allocator)
|
||||
}
|
||||
if index, ok := find_record(ctx, identity); ok {
|
||||
delete(identity, ctx.allocator)
|
||||
return index
|
||||
}
|
||||
name := fmt.aprintf("%s", preferred_name, allocator=ctx.allocator)
|
||||
if len(name) == 0 {
|
||||
delete(name, ctx.allocator)
|
||||
name = clone_cx_string(ctx.api, ctx.api.get_cursor_spelling(declaration), ctx.allocator)
|
||||
}
|
||||
index := u32(len(ctx.result.records))
|
||||
append(&ctx.result.records, Record{name=name, identity=identity})
|
||||
return index
|
||||
}
|
||||
|
||||
translate_type :: proc(ctx: ^Context, value: CXType, preferred_record_name := "", depth := 0) -> Type_Id {
|
||||
if depth > 64 || value.kind == CXType_Invalid || ctx.api.is_volatile_qualified_type(value) != 0 {
|
||||
return INVALID_TYPE
|
||||
}
|
||||
switch value.kind {
|
||||
case CXType_Void: return add_type(ctx, Type{kind=.Void, child=INVALID_TYPE})
|
||||
case CXType_Char_U: return add_type(ctx, Type{kind=.C_Char, child=INVALID_TYPE})
|
||||
case CXType_Char_S: return add_type(ctx, Type{kind=.C_Char, child=INVALID_TYPE})
|
||||
case CXType_SChar: return add_type(ctx, Type{kind=.C_Schar, child=INVALID_TYPE})
|
||||
case CXType_UChar: return add_type(ctx, Type{kind=.C_Uchar, child=INVALID_TYPE})
|
||||
case CXType_Short: return add_type(ctx, Type{kind=.C_Short, child=INVALID_TYPE})
|
||||
case CXType_UShort: return add_type(ctx, Type{kind=.C_Ushort, child=INVALID_TYPE})
|
||||
case CXType_Int: return add_type(ctx, Type{kind=.C_Int, child=INVALID_TYPE})
|
||||
case CXType_UInt: return add_type(ctx, Type{kind=.C_Uint, child=INVALID_TYPE})
|
||||
case CXType_Long: return add_type(ctx, Type{kind=.C_Long, child=INVALID_TYPE})
|
||||
case CXType_ULong: return add_type(ctx, Type{kind=.C_Ulong, child=INVALID_TYPE})
|
||||
case CXType_LongLong: return add_type(ctx, Type{kind=.C_Longlong, child=INVALID_TYPE})
|
||||
case CXType_ULongLong: return add_type(ctx, Type{kind=.C_Ulonglong, child=INVALID_TYPE})
|
||||
case CXType_Float: return add_type(ctx, Type{kind=.C_Float, child=INVALID_TYPE})
|
||||
case CXType_Double: return add_type(ctx, Type{kind=.C_Double, child=INVALID_TYPE})
|
||||
case CXType_LongDouble: return add_type(ctx, Type{kind=.C_Longdouble, child=INVALID_TYPE})
|
||||
case CXType_Pointer:
|
||||
pointee := ctx.api.get_pointee_type(value)
|
||||
child := translate_type(ctx, pointee, "", depth+1)
|
||||
if child == INVALID_TYPE {
|
||||
return INVALID_TYPE
|
||||
}
|
||||
return add_type(ctx, Type{
|
||||
kind=.Pointer,
|
||||
child=child,
|
||||
mutable=ctx.api.is_const_qualified_type(pointee) == 0,
|
||||
})
|
||||
case CXType_Record:
|
||||
declaration := ctx.api.get_type_declaration(value)
|
||||
if ctx.api.get_cursor_kind(declaration) == CXCursor_UnionDecl {
|
||||
return INVALID_TYPE
|
||||
}
|
||||
record := add_record(ctx, declaration, preferred_record_name)
|
||||
return add_type(ctx, Type{kind=.Record, child=INVALID_TYPE, record=record})
|
||||
case CXType_Typedef:
|
||||
declaration := ctx.api.get_type_declaration(value)
|
||||
name := clone_cx_string(ctx.api, ctx.api.get_cursor_spelling(declaration), ctx.allocator)
|
||||
defer delete(name, ctx.allocator)
|
||||
return translate_type(ctx, ctx.api.get_typedef_underlying_type(declaration), name, depth+1)
|
||||
case CXType_Elaborated, CXType_Attributed, CXType_Unexposed:
|
||||
canonical := ctx.api.get_canonical_type(value)
|
||||
if canonical.kind == value.kind {
|
||||
return INVALID_TYPE
|
||||
}
|
||||
return translate_type(ctx, canonical, preferred_record_name, depth+1)
|
||||
case CXType_Enum, CXType_FunctionNoProto, CXType_FunctionProto:
|
||||
return INVALID_TYPE
|
||||
}
|
||||
return INVALID_TYPE
|
||||
}
|
||||
|
||||
has_named :: proc(items: []Unsupported, name: string) -> bool {
|
||||
for item in items {
|
||||
if item.name == name {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
add_unsupported :: proc(ctx: ^Context, name, reason: string) {
|
||||
if len(name) == 0 || strings.has_prefix(name, "__") || has_named(ctx.result.unsupported[:], name) {
|
||||
return
|
||||
}
|
||||
append(&ctx.result.unsupported, Unsupported{
|
||||
name=fmt.aprintf("%s", name, allocator=ctx.allocator),
|
||||
reason=fmt.aprintf("%s", reason, allocator=ctx.allocator),
|
||||
})
|
||||
}
|
||||
|
||||
add_alias :: proc(ctx: ^Context, name: string, value: Type_Id, reason := "") {
|
||||
if len(name) == 0 {
|
||||
return
|
||||
}
|
||||
for alias in ctx.result.aliases {
|
||||
if alias.name == name {
|
||||
return
|
||||
}
|
||||
}
|
||||
append(&ctx.result.aliases, Alias{
|
||||
name=fmt.aprintf("%s", name, allocator=ctx.allocator),
|
||||
type=value,
|
||||
reason=fmt.aprintf("%s", reason, allocator=ctx.allocator),
|
||||
})
|
||||
}
|
||||
|
||||
visit_cursor :: proc "c"(cursor, parent: CXCursor, client_data: rawptr) -> i32 {
|
||||
context = runtime.default_context()
|
||||
ctx := (^Context)(client_data)
|
||||
kind := ctx.api.get_cursor_kind(cursor)
|
||||
name := clone_cx_string(ctx.api, ctx.api.get_cursor_spelling(cursor), ctx.allocator)
|
||||
defer delete(name, ctx.allocator)
|
||||
switch kind {
|
||||
case CXCursor_FunctionDecl:
|
||||
if len(name) == 0 {
|
||||
return CXChildVisit_Continue
|
||||
}
|
||||
reason := ""
|
||||
linkage := ctx.api.get_cursor_linkage(cursor)
|
||||
if linkage != CXLinkage_External {
|
||||
reason = "static and non-external C functions are not supported"
|
||||
} else if ctx.api.cursor_is_variadic(cursor) != 0 {
|
||||
reason = "C variadic functions are not supported"
|
||||
}
|
||||
function_type := ctx.api.get_cursor_type(cursor)
|
||||
result_type := translate_type(ctx, ctx.api.get_result_type(function_type))
|
||||
if result_type == INVALID_TYPE && len(reason) == 0 {
|
||||
reason = "function result type is not supported"
|
||||
}
|
||||
params: [dynamic]Type_Id
|
||||
params.allocator = ctx.allocator
|
||||
count := ctx.api.get_num_arg_types(function_type)
|
||||
if count < 0 {
|
||||
reason = "function declaration has no prototype"
|
||||
} else {
|
||||
for index in 0..<count {
|
||||
param := translate_type(ctx, ctx.api.get_arg_type(function_type, u32(index)))
|
||||
append(¶ms, param)
|
||||
if param == INVALID_TYPE && len(reason) == 0 {
|
||||
reason = "function parameter type is not supported"
|
||||
}
|
||||
}
|
||||
}
|
||||
append(&ctx.result.functions, Function{
|
||||
name=fmt.aprintf("%s", name, allocator=ctx.allocator),
|
||||
params=params[:],
|
||||
result=result_type,
|
||||
reason=fmt.aprintf("%s", reason, allocator=ctx.allocator),
|
||||
})
|
||||
case CXCursor_TypedefDecl:
|
||||
value := translate_type(ctx, ctx.api.get_typedef_underlying_type(cursor), name)
|
||||
reason := ""
|
||||
if value == INVALID_TYPE {
|
||||
reason = "typedef underlying type is not supported"
|
||||
}
|
||||
add_alias(ctx, name, value, reason)
|
||||
case CXCursor_StructDecl:
|
||||
if len(name) > 0 {
|
||||
record := add_record(ctx, cursor, name)
|
||||
value := add_type(ctx, Type{kind=.Record, child=INVALID_TYPE, record=record})
|
||||
add_alias(ctx, name, value)
|
||||
}
|
||||
case CXCursor_UnionDecl:
|
||||
add_unsupported(ctx, name, "C unions are not supported")
|
||||
case CXCursor_EnumDecl:
|
||||
add_unsupported(ctx, name, "C enums are not supported")
|
||||
case CXCursor_VarDecl:
|
||||
add_unsupported(ctx, name, "external C variables are not supported")
|
||||
case CXCursor_MacroDefinition:
|
||||
add_unsupported(ctx, name, "C macros are not supported")
|
||||
case:
|
||||
}
|
||||
return CXChildVisit_Continue
|
||||
}
|
||||
|
||||
import_with_libclang :: proc(_: rawptr, request: Request, allocator: mem.Allocator) -> Result {
|
||||
result := init_result(allocator)
|
||||
api, loaded := load_api()
|
||||
if !loaded {
|
||||
result.infrastructure = true
|
||||
result.error_message = fmt.aprintf(
|
||||
"could not load libclang; set BROLANG_LIBCLANG_PATH to a compatible library",
|
||||
allocator=allocator,
|
||||
)
|
||||
return result
|
||||
}
|
||||
defer _ = dynlib.unload_library(api.library)
|
||||
|
||||
index := api.create_index(1, 0)
|
||||
if index == nil {
|
||||
result.infrastructure = true
|
||||
result.error_message = fmt.aprintf("could not create libclang index", allocator=allocator)
|
||||
return result
|
||||
}
|
||||
defer api.dispose_index(index)
|
||||
|
||||
arguments: [dynamic]string
|
||||
arguments.allocator = context.temp_allocator
|
||||
append(&arguments, "-x", "c", "-target", target.llvm_triple(request.target))
|
||||
for path in request.include_paths {
|
||||
append(&arguments, fmt.tprintf("-I%s", path))
|
||||
}
|
||||
for define in request.defines {
|
||||
append(&arguments, fmt.tprintf("-D%s", define))
|
||||
}
|
||||
c_arguments := make([]cstring, len(arguments), context.temp_allocator)
|
||||
for argument, argument_index in arguments {
|
||||
c_arguments[argument_index] = strings.clone_to_cstring(argument, context.temp_allocator)
|
||||
}
|
||||
c_path := strings.clone_to_cstring(request.path, context.temp_allocator)
|
||||
translation_unit: CXTranslationUnit
|
||||
error_code := api.parse_translation_unit(
|
||||
index,
|
||||
c_path,
|
||||
raw_data(c_arguments),
|
||||
i32(len(c_arguments)),
|
||||
nil,
|
||||
0,
|
||||
CXTranslationUnit_DetailedPreprocessingRecord | CXTranslationUnit_SkipFunctionBodies | CXTranslationUnit_KeepGoing,
|
||||
&translation_unit,
|
||||
)
|
||||
if error_code != 0 || translation_unit == nil {
|
||||
result.error_message = fmt.aprintf("libclang could not parse header '%s'", request.path, allocator=allocator)
|
||||
return result
|
||||
}
|
||||
defer api.dispose_translation_unit(translation_unit)
|
||||
|
||||
for diagnostic_index in 0..<api.get_num_diagnostics(translation_unit) {
|
||||
diagnostic := api.get_diagnostic(translation_unit, diagnostic_index)
|
||||
severity := api.get_diagnostic_severity(diagnostic)
|
||||
if severity >= CXDiagnostic_Error && len(result.error_message) == 0 {
|
||||
result.error_message = clone_cx_string(&api, api.get_diagnostic_spelling(diagnostic), allocator)
|
||||
}
|
||||
api.dispose_diagnostic(diagnostic)
|
||||
}
|
||||
if len(result.error_message) > 0 {
|
||||
return result
|
||||
}
|
||||
|
||||
ctx := Context{api=&api, result=&result, allocator=allocator}
|
||||
root := api.get_translation_unit_cursor(translation_unit)
|
||||
_ = api.visit_children(root, visit_cursor, &ctx)
|
||||
result.available = true
|
||||
return result
|
||||
}
|
||||
Reference in New Issue
Block a user