export c header file for build library
This commit is contained in:
642
internal/header/header.go
Normal file
642
internal/header/header.go
Normal file
@@ -0,0 +1,642 @@
|
||||
/*
|
||||
* Copyright (c) 2024 The GoPlus Authors (goplus.org). All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package header
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"go/types"
|
||||
"io"
|
||||
"os"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/goplus/llgo/ssa"
|
||||
)
|
||||
|
||||
// cheaderWriter handles C header generation with type definition management
|
||||
type cheaderWriter struct {
|
||||
p ssa.Program
|
||||
typeBuf *bytes.Buffer // buffer for type definitions
|
||||
funcBuf *bytes.Buffer // buffer for function declarations
|
||||
declaredTypes map[string]bool // track declared types to avoid duplicates
|
||||
}
|
||||
|
||||
// newCHeaderWriter creates a new C header writer
|
||||
func newCHeaderWriter(p ssa.Program) *cheaderWriter {
|
||||
return &cheaderWriter{
|
||||
p: p,
|
||||
typeBuf: &bytes.Buffer{},
|
||||
funcBuf: &bytes.Buffer{},
|
||||
declaredTypes: make(map[string]bool),
|
||||
}
|
||||
}
|
||||
|
||||
// writeTypedef writes a C typedef for the given Go type if not already declared
|
||||
func (hw *cheaderWriter) writeTypedef(t types.Type) error {
|
||||
return hw.writeTypedefRecursive(t, make(map[string]bool))
|
||||
}
|
||||
|
||||
// writeTypedefRecursive writes typedefs recursively, handling dependencies
|
||||
func (hw *cheaderWriter) writeTypedefRecursive(t types.Type, visiting map[string]bool) error {
|
||||
// Handle container types that only need element processing
|
||||
switch typ := t.(type) {
|
||||
case *types.Array:
|
||||
return hw.writeTypedefRecursive(typ.Elem(), visiting)
|
||||
case *types.Slice:
|
||||
return hw.writeTypedefRecursive(typ.Elem(), visiting)
|
||||
case *types.Map:
|
||||
if err := hw.writeTypedefRecursive(typ.Key(), visiting); err != nil {
|
||||
return err
|
||||
}
|
||||
return hw.writeTypedefRecursive(typ.Elem(), visiting)
|
||||
case *types.Chan:
|
||||
return hw.writeTypedefRecursive(typ.Elem(), visiting)
|
||||
}
|
||||
|
||||
cType := hw.goCTypeName(t)
|
||||
if cType == "" || hw.declaredTypes[cType] {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Prevent infinite recursion for self-referential types
|
||||
if visiting[cType] {
|
||||
return nil
|
||||
}
|
||||
visiting[cType] = true
|
||||
defer delete(visiting, cType)
|
||||
|
||||
// Process dependent types for complex types
|
||||
if err := hw.processDependentTypes(t, visiting); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Then write the typedef for this type
|
||||
typedef := hw.generateTypedef(t)
|
||||
if typedef != "" {
|
||||
fmt.Fprintln(hw.typeBuf, typedef)
|
||||
// Add empty line after each type definition
|
||||
fmt.Fprintln(hw.typeBuf)
|
||||
hw.declaredTypes[cType] = true
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// processDependentTypes processes dependent types for composite types
|
||||
func (hw *cheaderWriter) processDependentTypes(t types.Type, visiting map[string]bool) error {
|
||||
switch typ := t.(type) {
|
||||
case *types.Pointer:
|
||||
return hw.writeTypedefRecursive(typ.Elem(), visiting)
|
||||
case *types.Struct:
|
||||
// For anonymous structs, handle field dependencies
|
||||
for i := 0; i < typ.NumFields(); i++ {
|
||||
field := typ.Field(i)
|
||||
if err := hw.writeTypedefRecursive(field.Type(), visiting); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
case *types.Named:
|
||||
// For named types, handle the underlying type dependencies
|
||||
underlying := typ.Underlying()
|
||||
if structType, ok := underlying.(*types.Struct); ok {
|
||||
// For named struct types, handle field dependencies directly
|
||||
for i := 0; i < structType.NumFields(); i++ {
|
||||
field := structType.Field(i)
|
||||
if err := hw.writeTypedefRecursive(field.Type(), visiting); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// For other named types, handle the underlying type
|
||||
return hw.writeTypedefRecursive(underlying, visiting)
|
||||
}
|
||||
case *types.Signature:
|
||||
return hw.processSignatureTypes(typ, visiting)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// processSignatureTypes processes function signature parameter and result types
|
||||
func (hw *cheaderWriter) processSignatureTypes(sig *types.Signature, visiting map[string]bool) error {
|
||||
// Handle function parameters
|
||||
if sig.Params() != nil {
|
||||
for i := 0; i < sig.Params().Len(); i++ {
|
||||
param := sig.Params().At(i)
|
||||
if err := hw.writeTypedefRecursive(param.Type(), visiting); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
// Handle function results
|
||||
if sig.Results() != nil {
|
||||
for i := 0; i < sig.Results().Len(); i++ {
|
||||
result := sig.Results().At(i)
|
||||
if err := hw.writeTypedefRecursive(result.Type(), visiting); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
return nil
|
||||
}
|
||||
|
||||
// goCTypeName returns the C type name for a Go type
|
||||
func (hw *cheaderWriter) goCTypeName(t types.Type) string {
|
||||
switch typ := t.(type) {
|
||||
case *types.Basic:
|
||||
switch typ.Kind() {
|
||||
case types.Invalid:
|
||||
return ""
|
||||
case types.Bool:
|
||||
return "_Bool"
|
||||
case types.Int8:
|
||||
return "int8_t"
|
||||
case types.Uint8:
|
||||
return "uint8_t"
|
||||
case types.Int16:
|
||||
return "int16_t"
|
||||
case types.Uint16:
|
||||
return "uint16_t"
|
||||
case types.Int32:
|
||||
return "int32_t"
|
||||
case types.Uint32:
|
||||
return "uint32_t"
|
||||
case types.Int64:
|
||||
return "int64_t"
|
||||
case types.Uint64:
|
||||
return "uint64_t"
|
||||
case types.Int:
|
||||
return "intptr_t"
|
||||
case types.Uint:
|
||||
return "uintptr_t"
|
||||
case types.Uintptr:
|
||||
return "uintptr_t"
|
||||
case types.Float32:
|
||||
return "float"
|
||||
case types.Float64:
|
||||
return "double"
|
||||
case types.Complex64:
|
||||
return "GoComplex64"
|
||||
case types.Complex128:
|
||||
return "GoComplex128"
|
||||
case types.String:
|
||||
return "GoString"
|
||||
case types.UnsafePointer:
|
||||
return "void*"
|
||||
}
|
||||
case *types.Pointer:
|
||||
elemType := hw.goCTypeName(typ.Elem())
|
||||
if elemType == "" {
|
||||
return "void*"
|
||||
}
|
||||
return elemType + "*"
|
||||
case *types.Slice:
|
||||
return "GoSlice"
|
||||
case *types.Array:
|
||||
// For arrays, we return just the element type
|
||||
// The array size will be handled in field generation
|
||||
return hw.goCTypeName(typ.Elem())
|
||||
case *types.Map:
|
||||
return "GoMap"
|
||||
case *types.Chan:
|
||||
return "GoChan"
|
||||
case *types.Interface:
|
||||
return "GoInterface"
|
||||
case *types.Struct:
|
||||
// For anonymous structs, generate a descriptive name
|
||||
var fields []string
|
||||
for i := 0; i < typ.NumFields(); i++ {
|
||||
field := typ.Field(i)
|
||||
fieldType := hw.goCTypeName(field.Type())
|
||||
fields = append(fields, fmt.Sprintf("%s_%s", fieldType, field.Name()))
|
||||
}
|
||||
return fmt.Sprintf("struct_%s", strings.Join(fields, "_"))
|
||||
case *types.Named:
|
||||
// For named types, always use the named type
|
||||
pkg := typ.Obj().Pkg()
|
||||
return fmt.Sprintf("%s_%s", pkg.Name(), typ.Obj().Name())
|
||||
case *types.Signature:
|
||||
// Function types are represented as function pointers in C
|
||||
// For simplicity, we use void* to represent function pointers
|
||||
return "void*"
|
||||
}
|
||||
panic(fmt.Errorf("unsupported type: %v", t))
|
||||
}
|
||||
|
||||
// generateTypedef generates C typedef declaration for complex types
|
||||
func (hw *cheaderWriter) generateTypedef(t types.Type) string {
|
||||
switch typ := t.(type) {
|
||||
case *types.Struct:
|
||||
// Only generate typedef for anonymous structs
|
||||
return hw.generateStructTypedef(typ)
|
||||
case *types.Named:
|
||||
underlying := typ.Underlying()
|
||||
if structType, ok := underlying.(*types.Struct); ok {
|
||||
// For named struct types, generate the typedef directly
|
||||
return hw.generateNamedStructTypedef(typ, structType)
|
||||
}
|
||||
// For other named types, create a typedef to the underlying type
|
||||
underlyingCType := hw.goCTypeName(underlying)
|
||||
if underlyingCType != "" {
|
||||
cTypeName := hw.goCTypeName(typ)
|
||||
return fmt.Sprintf("typedef %s %s;", underlyingCType, cTypeName)
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// generateReturnType generates C return type, converting arrays to struct wrappers
|
||||
func (hw *cheaderWriter) generateReturnType(retType types.Type) string {
|
||||
switch typ := retType.(type) {
|
||||
case *types.Array:
|
||||
// For array return values, generate a struct wrapper
|
||||
return hw.ensureArrayStruct(typ)
|
||||
default:
|
||||
// For non-array types, use regular type conversion
|
||||
return hw.goCTypeName(retType)
|
||||
}
|
||||
}
|
||||
|
||||
// ensureArrayStruct generates array struct name and ensures its typedef is declared
|
||||
func (hw *cheaderWriter) ensureArrayStruct(arr *types.Array) string {
|
||||
// Generate struct name
|
||||
var dimensions []int64
|
||||
baseType := types.Type(arr)
|
||||
|
||||
// Traverse all array dimensions
|
||||
for {
|
||||
if a, ok := baseType.(*types.Array); ok {
|
||||
dimensions = append(dimensions, a.Len())
|
||||
baseType = a.Elem()
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Get base element type
|
||||
elemType := hw.goCTypeName(baseType)
|
||||
|
||||
// Generate struct name: Array_int32_t_4 for [4]int32, Array_int32_t_3_4 for [3][4]int32
|
||||
var name strings.Builder
|
||||
name.WriteString("Array_")
|
||||
name.WriteString(strings.ReplaceAll(elemType, "*", "_ptr"))
|
||||
for _, dim := range dimensions {
|
||||
name.WriteString(fmt.Sprintf("_%d", dim))
|
||||
}
|
||||
|
||||
structName := name.String()
|
||||
|
||||
// Ensure typedef is declared
|
||||
if !hw.declaredTypes[structName] {
|
||||
hw.declaredTypes[structName] = true
|
||||
// Generate field declaration for the array
|
||||
fieldDecl := hw.generateFieldDeclaration(arr, "data")
|
||||
// Write the typedef
|
||||
typedef := fmt.Sprintf("typedef struct {\n%s\n} %s;", fieldDecl, structName)
|
||||
fmt.Fprintf(hw.typeBuf, "%s\n\n", typedef)
|
||||
}
|
||||
|
||||
return structName
|
||||
}
|
||||
|
||||
// generateFieldDeclaration generates C field declaration with correct array syntax
|
||||
func (hw *cheaderWriter) generateFieldDeclaration(fieldType types.Type, fieldName string) string {
|
||||
switch fieldType.(type) {
|
||||
case *types.Array:
|
||||
// Handle multidimensional arrays by collecting all dimensions
|
||||
var dimensions []int64
|
||||
baseType := fieldType
|
||||
|
||||
// Traverse all array dimensions
|
||||
for {
|
||||
if arr, ok := baseType.(*types.Array); ok {
|
||||
dimensions = append(dimensions, arr.Len())
|
||||
baseType = arr.Elem()
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Get base element type
|
||||
elemType := hw.goCTypeName(baseType)
|
||||
|
||||
// Build array dimensions string [d1][d2][d3]...
|
||||
var dimStr strings.Builder
|
||||
for _, dim := range dimensions {
|
||||
dimStr.WriteString(fmt.Sprintf("[%d]", dim))
|
||||
}
|
||||
|
||||
return fmt.Sprintf(" %s %s%s;", elemType, fieldName, dimStr.String())
|
||||
default:
|
||||
cType := hw.goCTypeName(fieldType)
|
||||
return fmt.Sprintf(" %s %s;", cType, fieldName)
|
||||
}
|
||||
}
|
||||
|
||||
// generateStructTypedef generates typedef for anonymous struct
|
||||
func (hw *cheaderWriter) generateStructTypedef(s *types.Struct) string {
|
||||
// Generate descriptive type name inline
|
||||
var nameFields []string
|
||||
var declFields []string
|
||||
|
||||
for i := 0; i < s.NumFields(); i++ {
|
||||
field := s.Field(i)
|
||||
fieldType := hw.goCTypeName(field.Type())
|
||||
nameFields = append(nameFields, fmt.Sprintf("%s_%s", fieldType, field.Name()))
|
||||
declFields = append(declFields, hw.generateFieldDeclaration(field.Type(), field.Name()))
|
||||
}
|
||||
|
||||
typeName := fmt.Sprintf("struct_%s", strings.Join(nameFields, "_"))
|
||||
return fmt.Sprintf("typedef struct {\n%s\n} %s;", strings.Join(declFields, "\n"), typeName)
|
||||
}
|
||||
|
||||
// generateNamedStructTypedef generates typedef for named struct
|
||||
func (hw *cheaderWriter) generateNamedStructTypedef(named *types.Named, s *types.Struct) string {
|
||||
typeName := hw.goCTypeName(named)
|
||||
|
||||
// Check if this is a self-referential struct
|
||||
needsForwardDecl := hw.needsForwardDeclaration(s, typeName)
|
||||
var result string
|
||||
|
||||
if needsForwardDecl {
|
||||
// Add forward declaration
|
||||
result = fmt.Sprintf("typedef struct %s %s;\n", typeName, typeName)
|
||||
}
|
||||
|
||||
var fields []string
|
||||
for i := 0; i < s.NumFields(); i++ {
|
||||
field := s.Field(i)
|
||||
fields = append(fields, hw.generateFieldDeclaration(field.Type(), field.Name()))
|
||||
}
|
||||
|
||||
if needsForwardDecl {
|
||||
// Use struct tag in definition
|
||||
result += fmt.Sprintf("struct %s {\n%s\n};", typeName, strings.Join(fields, "\n"))
|
||||
} else {
|
||||
result = fmt.Sprintf("typedef struct {\n%s\n} %s;", strings.Join(fields, "\n"), typeName)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// needsForwardDeclaration checks if a struct needs forward declaration due to self-reference
|
||||
func (hw *cheaderWriter) needsForwardDeclaration(s *types.Struct, typeName string) bool {
|
||||
for i := 0; i < s.NumFields(); i++ {
|
||||
field := s.Field(i)
|
||||
if hw.typeReferencesSelf(field.Type(), typeName) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// typeReferencesSelf checks if a type references the given type name
|
||||
func (hw *cheaderWriter) typeReferencesSelf(t types.Type, selfTypeName string) bool {
|
||||
switch typ := t.(type) {
|
||||
case *types.Pointer:
|
||||
elemTypeName := hw.goCTypeName(typ.Elem())
|
||||
return elemTypeName == selfTypeName
|
||||
case *types.Slice:
|
||||
elemTypeName := hw.goCTypeName(typ.Elem())
|
||||
return elemTypeName == selfTypeName
|
||||
case *types.Array:
|
||||
elemTypeName := hw.goCTypeName(typ.Elem())
|
||||
return elemTypeName == selfTypeName
|
||||
case *types.Named:
|
||||
return hw.goCTypeName(typ) == selfTypeName
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// writeFunctionDecl writes C function declaration for exported Go function
|
||||
// fullName: the C function name to display in header
|
||||
// linkName: the actual Go function name for linking
|
||||
func (hw *cheaderWriter) writeFunctionDecl(fullName, linkName string, fn ssa.Function) error {
|
||||
if fn.IsNil() {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get Go signature from LLVM function type
|
||||
goType := fn.Type.RawType()
|
||||
sig, ok := goType.(*types.Signature)
|
||||
if !ok {
|
||||
return fmt.Errorf("function %s does not have signature type", fullName)
|
||||
}
|
||||
|
||||
// Generate return type
|
||||
var returnType string
|
||||
if sig.Results().Len() == 0 {
|
||||
returnType = "void"
|
||||
} else if sig.Results().Len() == 1 {
|
||||
retType := sig.Results().At(0).Type()
|
||||
if err := hw.writeTypedef(retType); err != nil {
|
||||
return err
|
||||
}
|
||||
returnType = hw.generateReturnType(retType)
|
||||
} else {
|
||||
return fmt.Errorf("function %s has more than one result", fullName)
|
||||
}
|
||||
|
||||
// Generate parameters
|
||||
var params []string
|
||||
for i := 0; i < sig.Params().Len(); i++ {
|
||||
param := sig.Params().At(i)
|
||||
paramType := param.Type()
|
||||
|
||||
if err := hw.writeTypedef(paramType); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
paramName := param.Name()
|
||||
if paramName == "" {
|
||||
paramName = fmt.Sprintf("param%d", i)
|
||||
}
|
||||
|
||||
// Use generateFieldDeclaration logic for consistent parameter syntax
|
||||
paramDecl := hw.generateFieldDeclaration(paramType, paramName)
|
||||
// Remove the leading spaces and semicolon to get just the declaration
|
||||
paramDecl = strings.TrimSpace(paramDecl)
|
||||
paramDecl = strings.TrimSuffix(paramDecl, ";")
|
||||
params = append(params, paramDecl)
|
||||
}
|
||||
|
||||
paramStr := strings.Join(params, ", ")
|
||||
if paramStr == "" {
|
||||
paramStr = "void"
|
||||
}
|
||||
// Write function declaration with return type on separate line for normal functions
|
||||
fmt.Fprintln(hw.funcBuf, returnType)
|
||||
// Generate function declaration using cross-platform macro when names differ
|
||||
var funcDecl string
|
||||
if fullName != linkName {
|
||||
funcDecl = fmt.Sprintf("%s(%s) GO_SYMBOL_RENAME(\"%s\")", fullName, paramStr, linkName)
|
||||
} else {
|
||||
funcDecl = fmt.Sprintf("%s(%s);", fullName, paramStr)
|
||||
}
|
||||
|
||||
fmt.Fprintln(hw.funcBuf, funcDecl)
|
||||
// Add empty line after each function declaration
|
||||
fmt.Fprintln(hw.funcBuf)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// writeCommonIncludes writes common C header includes and Go runtime type definitions
|
||||
func (hw *cheaderWriter) writeCommonIncludes() error {
|
||||
includes := `
|
||||
// Platform-specific symbol renaming macro
|
||||
#ifdef __APPLE__
|
||||
#define GO_SYMBOL_RENAME(go_name) __asm("_" go_name);
|
||||
#else
|
||||
#define GO_SYMBOL_RENAME(go_name) __asm(go_name);
|
||||
#endif
|
||||
|
||||
// Go runtime types
|
||||
typedef struct { const char *p; intptr_t n; } GoString;
|
||||
typedef struct { void *data; intptr_t len; intptr_t cap; } GoSlice;
|
||||
typedef struct { void *data; } GoMap;
|
||||
typedef struct { void *data; } GoChan;
|
||||
typedef struct { void *data; void *type; } GoInterface;
|
||||
typedef struct { float real; float imag; } GoComplex64;
|
||||
typedef struct { double real; double imag; } GoComplex128;
|
||||
|
||||
`
|
||||
|
||||
if _, err := hw.typeBuf.WriteString(includes); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// writeTo writes all generated content to the output writer
|
||||
func (hw *cheaderWriter) writeTo(w io.Writer) error {
|
||||
// Write type definitions first
|
||||
if hw.typeBuf.Len() > 0 {
|
||||
if _, err := hw.typeBuf.WriteTo(w); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Then write function declarations
|
||||
if hw.funcBuf.Len() > 0 {
|
||||
if _, err := hw.funcBuf.WriteTo(w); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func genHeader(p ssa.Program, pkgs []ssa.Package, w io.Writer) error {
|
||||
hw := newCHeaderWriter(p)
|
||||
|
||||
// Write common header includes and type definitions
|
||||
if err := hw.writeCommonIncludes(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Mark predefined Go types as declared
|
||||
hw.declaredTypes["GoString"] = true
|
||||
hw.declaredTypes["GoSlice"] = true
|
||||
hw.declaredTypes["GoMap"] = true
|
||||
hw.declaredTypes["GoChan"] = true
|
||||
hw.declaredTypes["GoInterface"] = true
|
||||
hw.declaredTypes["GoComplex64"] = true
|
||||
hw.declaredTypes["GoComplex128"] = true
|
||||
|
||||
// Process all exported functions
|
||||
for _, pkg := range pkgs {
|
||||
exports := pkg.ExportFuncs()
|
||||
// Sort functions for testing
|
||||
exportNames := make([]string, 0, len(exports))
|
||||
for name := range exports {
|
||||
exportNames = append(exportNames, name)
|
||||
}
|
||||
sort.Strings(exportNames)
|
||||
|
||||
for _, name := range exportNames { // name is goName
|
||||
link := exports[name] // link is cName
|
||||
fn := pkg.FuncOf(link)
|
||||
if fn == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// Write function declaration with proper C types
|
||||
if err := hw.writeFunctionDecl(link, link, fn); err != nil {
|
||||
return fmt.Errorf("failed to write declaration for function %s: %w", name, err)
|
||||
}
|
||||
}
|
||||
|
||||
initFnName := pkg.Path() + ".init"
|
||||
initFn := pkg.FuncOf(initFnName)
|
||||
if initFn != nil {
|
||||
// Generate C-compatible function name (replace . and / with _)
|
||||
cInitFnName := strings.ReplaceAll(strings.ReplaceAll(initFnName, ".", "_"), "/", "_")
|
||||
if err := hw.writeFunctionDecl(cInitFnName, initFnName, initFn); err != nil {
|
||||
return fmt.Errorf("failed to write declaration for function %s: %w", initFnName, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Write all content to output in the correct order
|
||||
return hw.writeTo(w)
|
||||
}
|
||||
|
||||
func GenHeaderFile(p ssa.Program, pkgs []ssa.Package, libName, headerPath string, verbose bool) error {
|
||||
// Write header file
|
||||
w, err := os.Create(headerPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to write header file %s: %w", headerPath, err)
|
||||
}
|
||||
defer w.Close()
|
||||
|
||||
if verbose {
|
||||
fmt.Fprintf(os.Stderr, "Generated C header: %s\n", headerPath)
|
||||
}
|
||||
|
||||
headerIdent := strings.ToUpper(strings.ReplaceAll(libName, "-", "_"))
|
||||
headerContent := fmt.Sprintf(`/* Code generated by llgo; DO NOT EDIT. */
|
||||
|
||||
#ifndef __%s_H_
|
||||
#define __%s_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
`, headerIdent, headerIdent)
|
||||
|
||||
w.Write([]byte(headerContent))
|
||||
|
||||
if err = genHeader(p, pkgs, w); err != nil {
|
||||
return fmt.Errorf("failed to generate header content for %s: %w", libName, err)
|
||||
}
|
||||
|
||||
footerContent := fmt.Sprintf(`
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __%s_H_ */
|
||||
`, headerIdent)
|
||||
|
||||
_, err = w.Write([]byte(footerContent))
|
||||
return err
|
||||
}
|
||||
Reference in New Issue
Block a user