7.2 KiB
7.2 KiB
llcppg Design
Usage
llcppg [config-file]
If config-file is not specified, a llcppg.cfg file is used in current directory. The configuration file format is as follows:
{
"name": "inih",
"cflags": "$(pkg-config --cflags inireader)",
"include": [
"INIReader.h",
"AnotherHeaderFile.h"
],
"libs": "$(pkg-config --libs inireader)",
"trimPrefixes": ["Ini", "INI"]
}
Steps
- llcppsymg: Generate symbol table for a C/C++ library
- Manually modify the desired Go symbols in symbol table
- llcppsigfetch: Fetch information of C/C++ symbols
- gogensig: Generate a go package by information of symbols
llcppsymg
llcppsymg config-file
llcppsymg - # read config from stdin
It generates a symbol table file named llcppg.symb.json. Its file format is as follows:
[
{
"mangle": "_ZN9INIReaderC1EPKcm",
"c++": "INIReader::INIReader(char const*, unsigned long)",
"go": "(*Reader).Init__0"
}
]
llcppsigfetch
llcppsigfetch config-file
llcppsigfetch - # read config from stdin
It fetches information of C/C++ symbols and print to stdout. Its format is as follows:
TODO: see llgo/xtool/clang/ast
gogensig
gogensig ast-file
gogensig - # read AST from stdin
Overall
Process
- The Parsing Module reads
llcppg.cfgto obtain dynamic libraries, header files, and the package name. After parsing, it writes the generatedllcppg.symb.jsonpath intollcppg.cfg. - The Function Declaration Generation Module reads
llcppg.cfgto get the package name, header files, and the previously generatedllcppg.symb.json. After parsing, it generates the function prototypellcppg.function.json. - Reads the previously generated
llcppg.information.json, stores it as a structure, and uses gogen to generate code based on the structure.
Parsing Module
Input
Obtains the paths to header files and dynamic library files by reading the JSON file llcppg.cfg.
{
"name": "inih",
"cflags": "$(pkg-config --cflags INIReader)",
"include": [
"INIReader.h",
"AnotherHeaderFile.h"
],
"libs": "$(pkg-config --libs INIReader)",
"trimPrefixes": ["Ini", "INI"]
}
llcppsymg config-file
Implementation Steps
- Parse dylib and store:
// types.go
type CPPSymbol struct {
Symbol string `json:"symbol"`
Type string `json:"type"`
Name string `json:"name"`
}
// parser_dylib.go
func parseDylibSymbols(lib string) ([]common.CPPSymbol, error)
- Parse header files and store:
// common.go
type ASTInformation struct {
Namespace string `json:"namespace"`
Class string `json:"class"`
Name string `json:"name"`
BaseClasses []string `json:"baseClasses"`
ReturnType string `json:"returnType"`
Location string `json:"location"`
Parameters []Parameter `json:"parameters"`
Symbol string `json:"symbol"`
}
type Parameter struct {
Name string `json:"name"`
Type string `json:"type"`
}
// parser_ast.go
func parseHeaderFile(config types.Config) ([]common.ASTInformation, error)
- Cross-reference data from the first two steps to get the final output
// common.go
type SymbolInfo struct {
Mangle string `json:"mangle"` // C++ Symbol
CPP string `json:"c++"` // C++ function name
Go string `json:"go"` // Go function name
}
// common_symbols.go
func getCommonSymbols(dylibSymbols []common.CPPSymbol, astInfoList []common.ASTInformation) []common.SymbolInfo {
- Generate
llcppg.symb.jsonfile and store the JSON file path intollcppg.cfg
func generateJSON([]CommonSymbolInfo)
- Example
llcppg.symb.jsonfile
{
"FunctionName": "A::B::C",
"Symbol": "_ZN9INIReaderC1ERKNSt3__112basic_stringIcNS0_11char_traitsIcEENS0_9allocatorIcEEEE",
"Location": "a.h",
"UserFunctionName": "CFromA"
}
Function Declaration Generation Module
Input
No input required, directly reads the llcppg.cfg file
Implementation Steps
- Execute the executable
llcppsigfetch config-file
- Parse header files
// common.go
type ASTInformation struct {
Namespace string `json:"namespace"`
Class string `json:"class"`
Name string `json:"name"`
BaseClasses []string `json:"baseClasses"`
ReturnType string `json:"returnType"`
Location string `json:"location"`
Parameters []Parameter `json:"parameters"`
Symbol string `json:"symbol"`
}
type Parameter struct {
Name string `json:"name"`
Type string `json:"type"`
}
// parser_ast.go
func ParseHeaderFile(filePath string) ([]common.ASTInformation, error)
- Generate the final JSON mapping file
llcppg.information.json
func GenerateJSONFile(info []common.ASTInformation)
{
"functionName": "A::B::C",
"symbol": "_ZN9INIReaderC1ERKNSt3__112basic_stringIcNS0_11char_traitsIcEENS0_9allocatorIcEEEE",
"location": "a.h",
"returnType": "int",
"userFunctionName": "CFromA",
"parameters": [
{
"arg1": "int"
},
{
"arg2": "*char"
}
]
}
Code Generation Module
Input
No input required, directly reads llcppg.information.json file
Implementation Steps
- Execute the executable
gogensig ast-file
- Parse JSON file
// common.go
type HeaderFileInfo struct {
FunctionName string `json:"functionName"`
Symbol string `json:"symbol"`
Location string `json:"location"`
UserFunctionName string `json:"userFunctionName"`
Parameters map[string]string `json:"parameters"`
}
// parse_json.go
func ParseJSON(jsonFilePath string) ([]common.HeaderFileInfo, error)
- Generate code using the parsed structure with gogen
// generator.go
func GenerateCode(info []common.HeaderFileInfo) {
pkg := gogen.NewPackage("", PackageName, nil)
cm := comment(fmt.Sprintf("llgo:link %s %s", funcName1, symbol1))
pkg.NewFunc(recv, funcName, params, results, variadic).SetComments(pkg, cm).BodyStart(pkg).End()
}
Output
- Directory structure
package_name/
├── _demo
├── demo1.go
└── llgo_link.go
└── a.go
└── b.go
└── c.go
Note that demo1.go file needs to be written by the user
llgo_link.gois responsible for linking configuration
package inih
const (
LLGoFiles = "$(pkg-config --cflags INIReader): _wrap/reader.cpp"
LLGoPackage = "link: $(pkg-config --libs inih INIReader); -linih -lINIReader"
)
- Example content for
a.go
package inih
import (
_ "unsafe"
"github.com/goplus/llgo/c"
)
//go:linkname Parse C.ini_parse
func Parse(filename *c.Char, handler func(user c.Pointer, section *c.Char, name *c.Char, value *c.Char) c.Int, user c.Pointer) c.Int
//go:linkname ParseFile C.ini_parse_file
func ParseFile(file c.FilePtr, handler func(user c.Pointer, section *c.Char, name *c.Char, value *c.Char) c.Int, user c.Pointer) c.Int
//go:linkname ParseString C.ini_parse_string
func ParseString(str *c.Char, handler func(user c.Pointer, section *c.Char, name *c.Char, value *c.Char) c.Int, user c.Pointer) c.Int