llcppsigfetch:based on language configuration analysis

This commit is contained in:
luoliwoshang
2024-09-06 11:44:56 +08:00
parent ae71f3c186
commit 07519732a1
5 changed files with 82 additions and 32 deletions

View File

@@ -21,6 +21,7 @@ import (
"io" "io"
"os" "os"
"path/filepath" "path/filepath"
"strconv"
"strings" "strings"
"github.com/goplus/llgo/c" "github.com/goplus/llgo/c"
@@ -56,10 +57,14 @@ func printUsage() {
fmt.Println(" If not provided, uses default 'llcppg.cfg'") fmt.Println(" If not provided, uses default 'llcppg.cfg'")
fmt.Println("") fmt.Println("")
fmt.Println(" --extract: Extract information from a single file") fmt.Println(" --extract: Extract information from a single file")
fmt.Println(" <file>: When <temp> is false: the path to the file to process") fmt.Println(" <file>: Path to the file to process, or file content if -temp=true")
fmt.Println(" When <temp> is true: the content of the file to process") fmt.Println(" -temp=<bool>: Optional. Set to 'true' if <file> contains file content,")
fmt.Println(" <temp>: 'true' if <file> contains file content, 'false' if it's a file path") fmt.Println(" 'false' (default) if it's a file path")
fmt.Println(" [args]: Optional additional arguments (default: -x c++ -std=c++11)") fmt.Println(" -cpp=<bool>: Optional. Set to 'true' if the language is C++ (default: true)")
fmt.Println(" If not present, <file> is a file path")
fmt.Println(" [args]: Optional additional arguments")
fmt.Println(" Default for C++: -x c++")
fmt.Println(" Default for C: -x c")
fmt.Println("") fmt.Println("")
fmt.Println(" --help, -h: Show this help message") fmt.Println(" --help, -h: Show this help message")
fmt.Println("") fmt.Println("")
@@ -91,7 +96,7 @@ func runFromConfig() {
files := getHeaderFiles(conf.CFlags, conf.Include) files := getHeaderFiles(conf.CFlags, conf.Include)
context := parse.NewContext() context := parse.NewContext(conf.Cplusplus)
err = context.ProcessFiles(files) err = context.ProcessFiles(files)
check(err) check(err)
@@ -99,21 +104,33 @@ func runFromConfig() {
} }
func runExtract() { func runExtract() {
if len(os.Args) < 4 { if len(os.Args) < 3 {
fmt.Println("Error: Insufficient arguments for --extract")
printUsage() printUsage()
os.Exit(1) os.Exit(1)
} }
cfg := &parse.Config{ cfg := &parse.Config{
File: os.Args[2], File: os.Args[2],
Temp: os.Args[3] == "true", Args: []string{},
Args: os.Args[4:], IsCpp: true,
Temp: false,
} }
if !cfg.Temp {
absPath, err := filepath.Abs(cfg.File) for i := 3; i < len(os.Args); i++ {
check(err) arg := os.Args[i]
cfg.File = absPath switch {
println(cfg.File) case strings.HasPrefix(arg, "-temp="):
cfg.Temp = parseBoolArg(arg, "temp", false)
os.Args = append(os.Args[:i], os.Args[i+1:]...)
i--
case strings.HasPrefix(arg, "-cpp="):
cfg.IsCpp = parseBoolArg(arg, "cpp", true)
os.Args = append(os.Args[:i], os.Args[i+1:]...)
i--
default:
cfg.Args = append(cfg.Args, arg)
}
} }
converter, err := parse.NewConverter(cfg) converter, err := parse.NewConverter(cfg)
@@ -151,3 +168,17 @@ func outputInfo(context *parse.Context) {
defer info.Delete() defer info.Delete()
c.Printf(str) c.Printf(str)
} }
func parseBoolArg(arg, name string, defaultValue bool) bool {
parts := strings.SplitN(arg, "=", 2)
if len(parts) != 2 {
fmt.Printf("Warning: Invalid -%s= argument, defaulting to %v\n", name, defaultValue)
return defaultValue
}
value, err := strconv.ParseBool(parts[1])
if err != nil {
fmt.Printf("Warning: Invalid -%s= value '%s', defaulting to %v\n", name, parts[1], defaultValue)
return defaultValue
}
return value
}

View File

@@ -51,9 +51,10 @@ var tagMap = map[string]ast.Tag{
} }
type Config struct { type Config struct {
File string File string
Temp bool Temp bool
Args []string Args []string
IsCpp bool
} }
func NewConverter(config *Config) (*Converter, error) { func NewConverter(config *Config) (*Converter, error) {
@@ -72,12 +73,16 @@ func NewConverter(config *Config) (*Converter, error) {
} }
func CreateTranslationUnit(config *Config) (*clang.Index, *clang.TranslationUnit, error) { func CreateTranslationUnit(config *Config) (*clang.Index, *clang.TranslationUnit, error) {
if config.Args == nil || len(config.Args) == 0 { // default use the c/c++ standard of clang; c:gnu17 c++:gnu++17
config.Args = []string{"-x", "c++", "-std=c++11"} // https://clang.llvm.org/docs/CommandGuide/clang.html
defaultArgs := []string{"-x", "c"}
if config.IsCpp {
defaultArgs = []string{"-x", "c++"}
} }
allArgs := append(defaultArgs, config.Args...)
cArgs := make([]*c.Char, len(config.Args)) cArgs := make([]*c.Char, len(allArgs))
for i, arg := range config.Args { for i, arg := range allArgs {
cArgs[i] = c.AllocaCStr(arg) cArgs[i] = c.AllocaCStr(arg)
} }
@@ -418,6 +423,7 @@ func (ct *Converter) GenerateAnonymousName(cursor clang.Cursor) string {
} }
// converts functions, methods, constructors, destructors (including out-of-class decl) to ast.FuncDecl nodes. // converts functions, methods, constructors, destructors (including out-of-class decl) to ast.FuncDecl nodes.
// todo(zzy): manglename (c++)
func (ct *Converter) ProcessFuncDecl(cursor clang.Cursor) *ast.FuncDecl { func (ct *Converter) ProcessFuncDecl(cursor clang.Cursor) *ast.FuncDecl {
name := cursor.String() name := cursor.String()
defer name.Dispose() defer name.Dispose()

View File

@@ -13,8 +13,9 @@ import (
func RunTest(testName string, testCases []string) { func RunTest(testName string, testCases []string) {
for i, content := range testCases { for i, content := range testCases {
converter, err := parse.NewConverter(&parse.Config{ converter, err := parse.NewConverter(&parse.Config{
File: content, File: content,
Temp: true, Temp: true,
IsCpp: true,
}) })
if err != nil { if err != nil {
panic(err) panic(err)
@@ -45,9 +46,16 @@ type GetTypeOptions struct {
ExpectTypeKind clang.TypeKind ExpectTypeKind clang.TypeKind
// Args contains additional compilation arguments passed to Clang (optional) // Args contains additional compilation arguments passed to Clang (optional)
// Default is []string{"-x", "c++", "-std=c++11"} // These are appended after the default language-specific arguments
// *For complex C types, C language args Must be specified, e.g., []string{"-x", "c", "-std=c99"} // Example: []string{"-std=c++11"}
Args []string Args []string
// IsCpp indicates whether the code should be treated as C++ (true) or C (false)
// This affects the default language arguments passed to Clang:
// - For C++: []string{"-x", "c++"}
// - For C: []string{"-x", "c"}
// *For complex C types, C Must be specified
IsCpp bool
} }
// GetType returns the clang.Type of the given type code // GetType returns the clang.Type of the given type code
@@ -56,9 +64,10 @@ type GetTypeOptions struct {
func GetType(option *GetTypeOptions) (clang.Type, *clang.Index, *clang.TranslationUnit) { func GetType(option *GetTypeOptions) (clang.Type, *clang.Index, *clang.TranslationUnit) {
code := fmt.Sprintf("%s placeholder;", option.TypeCode) code := fmt.Sprintf("%s placeholder;", option.TypeCode)
index, unit, err := parse.CreateTranslationUnit(&parse.Config{ index, unit, err := parse.CreateTranslationUnit(&parse.Config{
File: code, File: code,
Temp: true, Temp: true,
Args: option.Args, Args: option.Args,
IsCpp: option.IsCpp,
}) })
if err != nil { if err != nil {
panic(err) panic(err)

View File

@@ -128,6 +128,7 @@ func TestNonBuiltinTypes() {
for _, t := range tests { for _, t := range tests {
typ, index, unit := test.GetType(&test.GetTypeOptions{ typ, index, unit := test.GetType(&test.GetTypeOptions{
TypeCode: t, TypeCode: t,
IsCpp: true,
}) })
converter := &parse.Converter{} converter := &parse.Converter{}
expr := converter.ProcessType(typ) expr := converter.ProcessType(typ)
@@ -167,7 +168,7 @@ func getComplexType(flag ast.TypeFlag) clang.Type {
typ, _, _ := test.GetType(&test.GetTypeOptions{ typ, _, _ := test.GetType(&test.GetTypeOptions{
TypeCode: code, TypeCode: code,
ExpectTypeKind: clang.TypeComplex, ExpectTypeKind: clang.TypeComplex,
Args: []string{"-x", "c", "-std=c99"}, IsCpp: false,
}) })
return typ return typ

View File

@@ -10,11 +10,13 @@ import (
type Context struct { type Context struct {
Files map[string]*ast.File Files map[string]*ast.File
IsCpp bool
} }
func NewContext() *Context { func NewContext(isCpp bool) *Context {
return &Context{ return &Context{
Files: make(map[string]*ast.File), Files: make(map[string]*ast.File),
IsCpp: isCpp,
} }
} }
@@ -60,8 +62,9 @@ func (p *Context) processFile(path string) error {
func (p *Context) parseFile(path string) (map[string]*ast.File, error) { func (p *Context) parseFile(path string) (map[string]*ast.File, error) {
converter, err := NewConverter(&Config{ converter, err := NewConverter(&Config{
File: path, File: path,
Temp: false, Temp: false,
IsCpp: p.IsCpp,
}) })
if err != nil { if err != nil {
return nil, errors.New("failed to create converter " + path) return nil, errors.New("failed to create converter " + path)