llcppsigfetch:-v

This commit is contained in:
luoliwoshang
2024-10-21 20:51:08 +08:00
parent 8840968e07
commit 72d176b77a
3 changed files with 205 additions and 17 deletions

View File

@@ -19,6 +19,7 @@ package main
import ( import (
"fmt" "fmt"
"io" "io"
"log"
"os" "os"
"path/filepath" "path/filepath"
"strconv" "strconv"
@@ -41,6 +42,10 @@ func main() {
printUsage() printUsage()
return return
} }
if ags.Verbose {
log.SetFlags(0)
parse.SetDebug(parse.DbgFlagAll)
}
extract := false extract := false
out := false out := false
@@ -74,9 +79,21 @@ func main() {
} }
if extract { if extract {
runExtract(extractFile, isTemp, isCpp, out, otherArgs) if ags.Verbose {
log.Println("runExtract: extractFile:", extractFile)
log.Println("isTemp:", isTemp)
log.Println("isCpp:", isCpp)
log.Println("out:", out)
log.Println("otherArgs:", otherArgs)
}
runExtract(extractFile, isTemp, isCpp, out, otherArgs, ags.Verbose)
} else { } else {
runFromConfig(ags.CfgFile, ags.UseStdin, out) if ags.Verbose {
log.Println("runFromConfig: config file:", ags.CfgFile)
log.Println("use stdin:", ags.UseStdin)
log.Println("output to file:", out)
}
runFromConfig(ags.CfgFile, ags.UseStdin, out, ags.Verbose)
} }
} }
@@ -109,7 +126,7 @@ func printUsage() {
fmt.Println("Note: The two usage modes are mutually exclusive. Use either [<config_file>] OR --extract, not both.") fmt.Println("Note: The two usage modes are mutually exclusive. Use either [<config_file>] OR --extract, not both.")
} }
func runFromConfig(cfgFile string, useStdin bool, outputToFile bool) { func runFromConfig(cfgFile string, useStdin bool, outputToFile bool, verbose bool) {
var data []byte var data []byte
var err error var err error
if useStdin { if useStdin {
@@ -117,6 +134,13 @@ func runFromConfig(cfgFile string, useStdin bool, outputToFile bool) {
} else { } else {
data, err = os.ReadFile(cfgFile) data, err = os.ReadFile(cfgFile)
} }
if verbose {
if useStdin {
log.Println("runFromConfig: read from stdin")
} else {
log.Println("runFromConfig: read from file", cfgFile)
}
}
check(err) check(err)
conf, err := config.GetConf(data) conf, err := config.GetConf(data)
@@ -124,10 +148,21 @@ func runFromConfig(cfgFile string, useStdin bool, outputToFile bool) {
defer conf.Delete() defer conf.Delete()
if err != nil { if err != nil {
fmt.Fprintln(os.Stderr, "Failed to parse config file:", cfgFile) log.Println("Failed to parse config file:", cfgFile)
os.Exit(1)
} }
files := getHeaderFiles(conf.CFlags, conf.Include) //todo(zzy): reuse the llcppsymg's cflags parse
cflag := ParseCFlags(conf.CFlags)
files, notFounds, err := cflag.GenHeaderFilePaths(conf.Include)
check(err)
if verbose {
log.Println("runFromConfig: header file paths", files)
if len(notFounds) > 0 {
log.Println("runFromConfig: not found header files", notFounds)
}
}
context := parse.NewContext(conf.Cplusplus) context := parse.NewContext(conf.Cplusplus)
err = context.ProcessFiles(files) err = context.ProcessFiles(files)
@@ -136,7 +171,7 @@ func runFromConfig(cfgFile string, useStdin bool, outputToFile bool) {
outputInfo(context, outputToFile) outputInfo(context, outputToFile)
} }
func runExtract(file string, isTemp bool, isCpp bool, outToFile bool, otherArgs []string) { func runExtract(file string, isTemp bool, isCpp bool, outToFile bool, otherArgs []string, verbose bool) {
cfg := &clangutils.Config{ cfg := &clangutils.Config{
File: file, File: file,
Args: otherArgs, Args: otherArgs,
@@ -175,14 +210,46 @@ func outputResult(result *c.Char, outputToFile bool) {
} }
} }
func getHeaderFiles(cflags string, files []string) []string { // todo(zzy): reuse the llcppsymg's cflags parse https://github.com/goplus/llgo/pull/788
prefix := cflags type CFlags struct {
prefix = strings.TrimPrefix(prefix, "-I") Paths []string // Include Path
var paths []string }
for _, f := range files {
paths = append(paths, filepath.Join(prefix, f)) func ParseCFlags(cflags string) *CFlags {
parts := strings.Fields(cflags)
cf := &CFlags{}
for _, part := range parts {
if strings.HasPrefix(part, "-I") {
cf.Paths = append(cf.Paths, part[2:])
} }
return paths }
return cf
}
func (cf *CFlags) GenHeaderFilePaths(files []string) ([]string, []string, error) {
var foundPaths []string
var notFound []string
for _, file := range files {
var found bool
for _, path := range cf.Paths {
fullPath := filepath.Join(path, file)
if _, err := os.Stat(fullPath); err == nil {
foundPaths = append(foundPaths, fullPath)
found = true
break
}
}
if !found {
notFound = append(notFound, file)
}
}
if len(foundPaths) == 0 {
return nil, notFound, fmt.Errorf("failed to find any header files")
}
return foundPaths, notFound, nil
} }
func outputInfo(context *parse.Context, outputToFile bool) { func outputInfo(context *parse.Context, outputToFile bool) {

View File

@@ -2,6 +2,7 @@ package parse
import ( import (
"fmt" "fmt"
"log"
"os" "os"
"strings" "strings"
"unsafe" "unsafe"
@@ -64,6 +65,13 @@ type Config struct {
} }
func NewConverter(config *clangutils.Config) (*Converter, error) { func NewConverter(config *clangutils.Config) (*Converter, error) {
if debugParse {
log.Println("NewConverter: config")
log.Println("config.File", config.File)
log.Println("config.Args", config.Args)
log.Println("config.IsCpp", config.IsCpp)
log.Println("config.Temp", config.Temp)
}
index, unit, err := clangutils.CreateTranslationUnit(config) index, unit, err := clangutils.CreateTranslationUnit(config)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -79,7 +87,13 @@ func NewConverter(config *clangutils.Config) (*Converter, error) {
} }
func (ct *Converter) Dispose() { func (ct *Converter) Dispose() {
if debugParse {
log.Println("Dispose: ct.index")
}
ct.index.Dispose() ct.index.Dispose()
if debugParse {
log.Println("Dispose: ct.unit")
}
ct.unit.Dispose() ct.unit.Dispose()
} }
@@ -123,14 +137,23 @@ func (ct *Converter) UpdateLoc(cursor clang.Cursor) {
func (ct *Converter) GetCurFile() *ast.File { func (ct *Converter) GetCurFile() *ast.File {
if ct.curLoc.File == "" { if ct.curLoc.File == "" {
if debugParse {
log.Println("GetCurFile: NO FILE")
}
return nil return nil
} }
// todo(zzy): more efficient // todo(zzy): more efficient
for i, entry := range ct.Files { for i, entry := range ct.Files {
if entry.Path == ct.curLoc.File { if entry.Path == ct.curLoc.File {
if debugParse {
log.Println("GetCurFile: found", ct.curLoc.File)
}
return ct.Files[i].Doc return ct.Files[i].Doc
} }
} }
if debugParse {
log.Println("GetCurFile: Create New ast.File", ct.curLoc.File)
}
newDoc := &ast.File{} newDoc := &ast.File{}
ct.Files = append(ct.Files, &FileEntry{Path: ct.curLoc.File, Doc: newDoc}) ct.Files = append(ct.Files, &FileEntry{Path: ct.curLoc.File, Doc: newDoc})
return newDoc return newDoc
@@ -219,6 +242,13 @@ func (ct *Converter) visitTop(cursor, parent clang.Cursor) clang.ChildVisitResul
ct.UpdateLoc(cursor) ct.UpdateLoc(cursor)
curFile := ct.GetCurFile() curFile := ct.GetCurFile()
if debugParse {
name := cursor.String()
defer name.Dispose()
log.Println("visitTop: Cursor:", c.GoString(name.CStr()))
}
if curFile == nil { if curFile == nil {
return clang.ChildVisit_Continue return clang.ChildVisit_Continue
} }
@@ -227,27 +257,69 @@ func (ct *Converter) visitTop(cursor, parent clang.Cursor) clang.ChildVisitResul
case clang.CursorInclusionDirective: case clang.CursorInclusionDirective:
include := ct.ProcessInclude(cursor) include := ct.ProcessInclude(cursor)
curFile.Includes = append(curFile.Includes, include) curFile.Includes = append(curFile.Includes, include)
if debugParse {
log.Println("visitTop: ProcessInclude END ", include.Path)
}
case clang.CursorMacroDefinition: case clang.CursorMacroDefinition:
macro := ct.ProcessMacro(cursor) macro := ct.ProcessMacro(cursor)
curFile.Macros = append(curFile.Macros, macro) curFile.Macros = append(curFile.Macros, macro)
if debugParse {
log.Println("visitTop: ProcessMacro END ", macro.Name, "Tokens Length:", len(macro.Tokens))
}
case clang.CursorEnumDecl: case clang.CursorEnumDecl:
enum := ct.ProcessEnumDecl(cursor) enum := ct.ProcessEnumDecl(cursor)
curFile.Decls = append(curFile.Decls, enum) curFile.Decls = append(curFile.Decls, enum)
if debugParse {
name := "anonymous"
if enum.Name != nil {
name = enum.Name.Name
}
log.Println("visitTop: ProcessEnumDecl END", name)
}
case clang.CursorClassDecl: case clang.CursorClassDecl:
classDecl := ct.ProcessClassDecl(cursor) classDecl := ct.ProcessClassDecl(cursor)
curFile.Decls = append(curFile.Decls, classDecl) curFile.Decls = append(curFile.Decls, classDecl)
if debugParse {
name := "anonymous"
if classDecl.Name != nil {
name = classDecl.Name.Name
}
log.Println("visitTop: ProcessClassDecl END", name)
}
case clang.CursorStructDecl: case clang.CursorStructDecl:
structDecl := ct.ProcessStructDecl(cursor) structDecl := ct.ProcessStructDecl(cursor)
curFile.Decls = append(curFile.Decls, structDecl) curFile.Decls = append(curFile.Decls, structDecl)
if debugParse {
name := "anonymous"
if structDecl.Name != nil {
name = structDecl.Name.Name
}
log.Println("visitTop: ProcessStructDecl END", name)
}
case clang.CursorUnionDecl: case clang.CursorUnionDecl:
unionDecl := ct.ProcessUnionDecl(cursor) unionDecl := ct.ProcessUnionDecl(cursor)
curFile.Decls = append(curFile.Decls, unionDecl) curFile.Decls = append(curFile.Decls, unionDecl)
if debugParse {
name := "anonymous"
if unionDecl.Name != nil {
name = unionDecl.Name.Name
}
log.Println("visitTop: ProcessUnionDecl END", name)
}
case clang.CursorFunctionDecl, clang.CursorCXXMethod, clang.CursorConstructor, clang.CursorDestructor: case clang.CursorFunctionDecl, clang.CursorCXXMethod, clang.CursorConstructor, clang.CursorDestructor:
// Handle functions and class methods (including out-of-class method) // Handle functions and class methods (including out-of-class method)
// Example: void MyClass::myMethod() { ... } out-of-class method // Example: void MyClass::myMethod() { ... } out-of-class method
curFile.Decls = append(curFile.Decls, ct.ProcessFuncDecl(cursor)) funcDecl := ct.ProcessFuncDecl(cursor)
curFile.Decls = append(curFile.Decls, funcDecl)
if debugParse {
log.Println("visitTop: ProcessFuncDecl END", funcDecl.Name.Name, funcDecl.MangledName, "isStatic:", funcDecl.IsStatic, "isInline:", funcDecl.IsInline)
}
case clang.CursorTypedefDecl: case clang.CursorTypedefDecl:
curFile.Decls = append(curFile.Decls, ct.ProcessTypeDefDecl(cursor)) typedefDecl := ct.ProcessTypeDefDecl(cursor)
curFile.Decls = append(curFile.Decls, typedefDecl)
if debugParse {
log.Println("visitTop: ProcessTypeDefDecl END", typedefDecl.Name.Name)
}
case clang.CursorNamespace: case clang.CursorNamespace:
VisitChildren(cursor, ct.visitTop) VisitChildren(cursor, ct.visitTop)
} }
@@ -352,12 +424,17 @@ func (ct *Converter) ProcessTypeDefDecl(cursor clang.Cursor) *ast.TypedefDecl {
} }
ct.SetTypeDecl(cursor, decl) ct.SetTypeDecl(cursor, decl)
return decl return decl
} }
func (ct *Converter) ProcessUnderlyingType(cursor clang.Cursor) ast.Expr { func (ct *Converter) ProcessUnderlyingType(cursor clang.Cursor) ast.Expr {
underlyingTyp := cursor.TypedefDeclUnderlyingType() underlyingTyp := cursor.TypedefDeclUnderlyingType()
if underlyingTyp.Kind != clang.TypeElaborated { if underlyingTyp.Kind != clang.TypeElaborated {
if debugParse {
log.Println("ProcessUnderlyingType: not elaborated")
}
return ct.ProcessType(underlyingTyp) return ct.ProcessType(underlyingTyp)
} }
@@ -370,6 +447,9 @@ func (ct *Converter) ProcessUnderlyingType(cursor clang.Cursor) ast.Expr {
// In this case, libclang incorrectly reports an anonymous struct as a named struct // In this case, libclang incorrectly reports an anonymous struct as a named struct
sourceCode := ct.GetTokens(referTypeCursor) sourceCode := ct.GetTokens(referTypeCursor)
if isAnonymousStructure(sourceCode) { if isAnonymousStructure(sourceCode) {
if debugParse {
log.Println("ProcessUnderlyingType: is anonymous structure")
}
ct.SetAnonyType(referTypeCursor) ct.SetAnonyType(referTypeCursor)
typ, isValidType := ct.GetTypeDecl(referTypeCursor) typ, isValidType := ct.GetTypeDecl(referTypeCursor)
if isValidType { if isValidType {
@@ -377,9 +457,15 @@ func (ct *Converter) ProcessUnderlyingType(cursor clang.Cursor) ast.Expr {
// according to a normal anonymous decl // according to a normal anonymous decl
switch declType := typ.(type) { switch declType := typ.(type) {
case *ast.EnumTypeDecl: case *ast.EnumTypeDecl:
if debugParse {
log.Println("ProcessUnderlyingType: is actually anonymous enum,remove name")
}
declType.Name = nil declType.Name = nil
case *ast.TypeDecl: case *ast.TypeDecl:
if declType.Type.Tag != ast.Class { if declType.Type.Tag != ast.Class {
if debugParse {
log.Println("ProcessUnderlyingType: is actually anonymous struct,remove name")
}
declType.Name = nil declType.Name = nil
} else { } else {
// Unreachable: There should be no anonymous classes in this context // Unreachable: There should be no anonymous classes in this context
@@ -405,11 +491,17 @@ func (ct *Converter) ProcessFuncDecl(cursor clang.Cursor) *ast.FuncDecl {
// function type will only collect return type // function type will only collect return type
// ProcessType can't get the field names,will collect in follows // ProcessType can't get the field names,will collect in follows
if debugParse {
log.Println("ProcessFuncDecl: Get Base FuncType")
}
funcType, ok := ct.ProcessType(cursor.Type()).(*ast.FuncType) funcType, ok := ct.ProcessType(cursor.Type()).(*ast.FuncType)
if !ok { if !ok {
fmt.Println("failed to process function type") fmt.Println("failed to process function type")
return nil return nil
} }
if debugParse {
log.Println("ProcessFuncDecl: ProcessFieldList")
}
params := ct.ProcessFieldList(cursor) params := ct.ProcessFieldList(cursor)
funcType.Params = params funcType.Params = params
@@ -428,6 +520,9 @@ func (ct *Converter) ProcessFuncDecl(cursor clang.Cursor) *ast.FuncDecl {
} }
if isMethod(cursor) { if isMethod(cursor) {
if debugParse {
log.Println("ProcessFuncDecl: is method, ProcessMethodAttributes")
}
ct.ProcessMethodAttributes(cursor, funcDecl) ct.ProcessMethodAttributes(cursor, funcDecl)
} else { } else {
if cursor.StorageClass() == clang.SCStatic { if cursor.StorageClass() == clang.SCStatic {
@@ -436,7 +531,6 @@ func (ct *Converter) ProcessFuncDecl(cursor clang.Cursor) *ast.FuncDecl {
} }
ct.SetTypeDecl(cursor, funcDecl) ct.SetTypeDecl(cursor, funcDecl)
return funcDecl return funcDecl
} }
@@ -675,7 +769,6 @@ func (ct *Converter) ProcessClassDecl(cursor clang.Cursor) *ast.TypeDecl {
Type: typ, Type: typ,
} }
ct.SetTypeDecl(cursor, decl) ct.SetTypeDecl(cursor, decl)
return decl return decl
} }

View File

@@ -2,11 +2,27 @@ package parse
import ( import (
"errors" "errors"
"log"
"github.com/goplus/llgo/c/cjson" "github.com/goplus/llgo/c/cjson"
"github.com/goplus/llgo/chore/_xtool/llcppsymg/clangutils" "github.com/goplus/llgo/chore/_xtool/llcppsymg/clangutils"
) )
type dbgFlags = int
const (
DbgParse dbgFlags = 1 << iota
DbgFlagAll = DbgParse
)
var (
debugParse bool
)
func SetDebug(dbgFlags dbgFlags) {
debugParse = (dbgFlags & DbgParse) != 0
}
type Context struct { type Context struct {
Files []*FileEntry Files []*FileEntry
IsCpp bool IsCpp bool
@@ -25,6 +41,9 @@ func (p *Context) Output() *cjson.JSON {
// ProcessFiles processes the given files and adds them to the context // ProcessFiles processes the given files and adds them to the context
func (p *Context) ProcessFiles(files []string) error { func (p *Context) ProcessFiles(files []string) error {
if debugParse {
log.Println("ProcessFiles: files", files, "isCpp", p.IsCpp)
}
for _, file := range files { for _, file := range files {
if err := p.processFile(file); err != nil { if err := p.processFile(file); err != nil {
return err return err
@@ -35,8 +54,14 @@ func (p *Context) ProcessFiles(files []string) error {
// parse file and add it to the context,avoid duplicate parsing // parse file and add it to the context,avoid duplicate parsing
func (p *Context) processFile(path string) error { func (p *Context) processFile(path string) error {
if debugParse {
log.Println("processFile: path", path)
}
for _, entry := range p.Files { for _, entry := range p.Files {
if entry.Path == path { if entry.Path == path {
if debugParse {
log.Println("processFile: already parsed", path)
}
return nil return nil
} }
} }
@@ -50,6 +75,9 @@ func (p *Context) processFile(path string) error {
} }
func (p *Context) parseFile(path string) ([]*FileEntry, error) { func (p *Context) parseFile(path string) ([]*FileEntry, error) {
if debugParse {
log.Println("parseFile: path", path)
}
converter, err := NewConverter(&clangutils.Config{ converter, err := NewConverter(&clangutils.Config{
File: path, File: path,
Temp: false, Temp: false,