diff --git a/chore/_xtool/llcppsigfetch/llcppsigfetch.go b/chore/_xtool/llcppsigfetch/llcppsigfetch.go index 0c35c638..847d6e27 100644 --- a/chore/_xtool/llcppsigfetch/llcppsigfetch.go +++ b/chore/_xtool/llcppsigfetch/llcppsigfetch.go @@ -19,6 +19,7 @@ package main import ( "fmt" "io" + "log" "os" "path/filepath" "strconv" @@ -41,6 +42,10 @@ func main() { printUsage() return } + if ags.Verbose { + log.SetFlags(0) + parse.SetDebug(parse.DbgFlagAll) + } extract := false out := false @@ -74,9 +79,21 @@ func main() { } 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 { - 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 [] 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 err error if useStdin { @@ -117,6 +134,13 @@ func runFromConfig(cfgFile string, useStdin bool, outputToFile bool) { } else { 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) conf, err := config.GetConf(data) @@ -124,10 +148,21 @@ func runFromConfig(cfgFile string, useStdin bool, outputToFile bool) { defer conf.Delete() 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) err = context.ProcessFiles(files) @@ -136,7 +171,7 @@ func runFromConfig(cfgFile string, useStdin bool, outputToFile bool) { 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{ File: file, Args: otherArgs, @@ -175,14 +210,46 @@ func outputResult(result *c.Char, outputToFile bool) { } } -func getHeaderFiles(cflags string, files []string) []string { - prefix := cflags - prefix = strings.TrimPrefix(prefix, "-I") - var paths []string - for _, f := range files { - paths = append(paths, filepath.Join(prefix, f)) +// todo(zzy): reuse the llcppsymg's cflags parse https://github.com/goplus/llgo/pull/788 +type CFlags struct { + Paths []string // Include Path +} + +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) { diff --git a/chore/_xtool/llcppsigfetch/parse/cvt.go b/chore/_xtool/llcppsigfetch/parse/cvt.go index fbeba192..836d4949 100644 --- a/chore/_xtool/llcppsigfetch/parse/cvt.go +++ b/chore/_xtool/llcppsigfetch/parse/cvt.go @@ -2,6 +2,7 @@ package parse import ( "fmt" + "log" "os" "strings" "unsafe" @@ -64,6 +65,13 @@ type Config struct { } 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) if err != nil { return nil, err @@ -79,7 +87,13 @@ func NewConverter(config *clangutils.Config) (*Converter, error) { } func (ct *Converter) Dispose() { + if debugParse { + log.Println("Dispose: ct.index") + } ct.index.Dispose() + if debugParse { + log.Println("Dispose: ct.unit") + } ct.unit.Dispose() } @@ -123,14 +137,23 @@ func (ct *Converter) UpdateLoc(cursor clang.Cursor) { func (ct *Converter) GetCurFile() *ast.File { if ct.curLoc.File == "" { + if debugParse { + log.Println("GetCurFile: NO FILE") + } return nil } // todo(zzy): more efficient for i, entry := range ct.Files { if entry.Path == ct.curLoc.File { + if debugParse { + log.Println("GetCurFile: found", ct.curLoc.File) + } return ct.Files[i].Doc } } + if debugParse { + log.Println("GetCurFile: Create New ast.File", ct.curLoc.File) + } newDoc := &ast.File{} ct.Files = append(ct.Files, &FileEntry{Path: ct.curLoc.File, Doc: newDoc}) return newDoc @@ -219,6 +242,13 @@ func (ct *Converter) visitTop(cursor, parent clang.Cursor) clang.ChildVisitResul ct.UpdateLoc(cursor) curFile := ct.GetCurFile() + + if debugParse { + name := cursor.String() + defer name.Dispose() + log.Println("visitTop: Cursor:", c.GoString(name.CStr())) + } + if curFile == nil { return clang.ChildVisit_Continue } @@ -227,27 +257,69 @@ func (ct *Converter) visitTop(cursor, parent clang.Cursor) clang.ChildVisitResul case clang.CursorInclusionDirective: include := ct.ProcessInclude(cursor) curFile.Includes = append(curFile.Includes, include) + if debugParse { + log.Println("visitTop: ProcessInclude END ", include.Path) + } case clang.CursorMacroDefinition: macro := ct.ProcessMacro(cursor) curFile.Macros = append(curFile.Macros, macro) + if debugParse { + log.Println("visitTop: ProcessMacro END ", macro.Name, "Tokens Length:", len(macro.Tokens)) + } case clang.CursorEnumDecl: enum := ct.ProcessEnumDecl(cursor) 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: classDecl := ct.ProcessClassDecl(cursor) 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: structDecl := ct.ProcessStructDecl(cursor) 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: unionDecl := ct.ProcessUnionDecl(cursor) 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: // Handle functions and class methods (including 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: - 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: VisitChildren(cursor, ct.visitTop) } @@ -352,12 +424,17 @@ func (ct *Converter) ProcessTypeDefDecl(cursor clang.Cursor) *ast.TypedefDecl { } ct.SetTypeDecl(cursor, decl) + return decl } func (ct *Converter) ProcessUnderlyingType(cursor clang.Cursor) ast.Expr { underlyingTyp := cursor.TypedefDeclUnderlyingType() + if underlyingTyp.Kind != clang.TypeElaborated { + if debugParse { + log.Println("ProcessUnderlyingType: not elaborated") + } 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 sourceCode := ct.GetTokens(referTypeCursor) if isAnonymousStructure(sourceCode) { + if debugParse { + log.Println("ProcessUnderlyingType: is anonymous structure") + } ct.SetAnonyType(referTypeCursor) typ, isValidType := ct.GetTypeDecl(referTypeCursor) if isValidType { @@ -377,9 +457,15 @@ func (ct *Converter) ProcessUnderlyingType(cursor clang.Cursor) ast.Expr { // according to a normal anonymous decl switch declType := typ.(type) { case *ast.EnumTypeDecl: + if debugParse { + log.Println("ProcessUnderlyingType: is actually anonymous enum,remove name") + } declType.Name = nil case *ast.TypeDecl: if declType.Type.Tag != ast.Class { + if debugParse { + log.Println("ProcessUnderlyingType: is actually anonymous struct,remove name") + } declType.Name = nil } else { // 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 // 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) if !ok { fmt.Println("failed to process function type") return nil } + if debugParse { + log.Println("ProcessFuncDecl: ProcessFieldList") + } params := ct.ProcessFieldList(cursor) funcType.Params = params @@ -428,6 +520,9 @@ func (ct *Converter) ProcessFuncDecl(cursor clang.Cursor) *ast.FuncDecl { } if isMethod(cursor) { + if debugParse { + log.Println("ProcessFuncDecl: is method, ProcessMethodAttributes") + } ct.ProcessMethodAttributes(cursor, funcDecl) } else { if cursor.StorageClass() == clang.SCStatic { @@ -436,7 +531,6 @@ func (ct *Converter) ProcessFuncDecl(cursor clang.Cursor) *ast.FuncDecl { } ct.SetTypeDecl(cursor, funcDecl) - return funcDecl } @@ -675,7 +769,6 @@ func (ct *Converter) ProcessClassDecl(cursor clang.Cursor) *ast.TypeDecl { Type: typ, } ct.SetTypeDecl(cursor, decl) - return decl } diff --git a/chore/_xtool/llcppsigfetch/parse/parse.go b/chore/_xtool/llcppsigfetch/parse/parse.go index a267d4fc..f76480cd 100644 --- a/chore/_xtool/llcppsigfetch/parse/parse.go +++ b/chore/_xtool/llcppsigfetch/parse/parse.go @@ -2,11 +2,27 @@ package parse import ( "errors" + "log" "github.com/goplus/llgo/c/cjson" "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 { Files []*FileEntry IsCpp bool @@ -25,6 +41,9 @@ func (p *Context) Output() *cjson.JSON { // ProcessFiles processes the given files and adds them to the context func (p *Context) ProcessFiles(files []string) error { + if debugParse { + log.Println("ProcessFiles: files", files, "isCpp", p.IsCpp) + } for _, file := range files { if err := p.processFile(file); err != nil { return err @@ -35,8 +54,14 @@ func (p *Context) ProcessFiles(files []string) error { // parse file and add it to the context,avoid duplicate parsing func (p *Context) processFile(path string) error { + if debugParse { + log.Println("processFile: path", path) + } for _, entry := range p.Files { if entry.Path == path { + if debugParse { + log.Println("processFile: already parsed", path) + } return nil } } @@ -50,6 +75,9 @@ func (p *Context) processFile(path string) error { } func (p *Context) parseFile(path string) ([]*FileEntry, error) { + if debugParse { + log.Println("parseFile: path", path) + } converter, err := NewConverter(&clangutils.Config{ File: path, Temp: false,