diff --git a/cl/class.go b/cl/class.go index b6fffc1d..cb0051bc 100644 --- a/cl/class.go +++ b/cl/class.go @@ -87,6 +87,10 @@ func newTypeParams(ctx *pkgCtx, pkg *types.Package, cls clang.Cursor) (ret []*ty clang.VisitChildren(cls, func(decl, parent clang.Cursor) clang.ChildVisitResult { switch decl.Kind { case lc.Cursor_TemplateTypeParameter: + if isParameterPack(decl) { + quietIgnore = true + return clang.Break + } idx++ name := clang.String(decl) if name == "" { @@ -105,6 +109,19 @@ func newTypeParams(ctx *pkgCtx, pkg *types.Package, cls clang.Cursor) (ret []*ty return } +func isParameterPack(decl clang.Cursor) bool { // + tu := clang.TranslationUnit{TranslationUnit: decl.TranslationUnit()} + extent := decl.Extent() + tokens, dispose := tu.Tokenize(extent) + defer dispose() + for _, token := range tokens { + if token.Kind() == lc.Token_Punctuation && tu.TokenSpelling(token) == "..." { + return true + } + } + return false +} + func loadTemplateClass(ctx *pkgCtx, cls clang.Cursor, parent *scopeCtx) { if obj, ok := ctx.addTemplateClass(ctx, cls); ok { ctx.addCompileUnit(func(ctx *pkgCtx) { @@ -125,9 +142,8 @@ func compileTemplateClass(ctx *pkgCtx, obj *templateClass, parent *scopeCtx) { pkgTypes := pkg.Types tparams, quietIgnore := newTypeParams(ctx, pkgTypes, cls) if quietIgnore || order >= 0 { - if debugQuietIgnore { - ctx.logf(cls, "class %s: unsupported template params, ignored", cName) - } + ctx.ignoref(featQuietIgnore, cls, "class %s: unsupported template params, ignored", cName) + ctx.ignoreType(cName, featQuietIgnore) return } @@ -474,13 +490,12 @@ func baseClass(ctx *pkgCtx, decl clang.Cursor, feats *int) *types.TypeName { t = t.Named() } switch t.Kind { - case lc.Type_Record: + case lc.Type_Record, lc.Type_Typedef: cName := cTypeName(t) - if t, ok := ctx.typeObj(cName); ok { - return t + if o, ok := ctx.getTypeObj(cName, feats); ok { + return o } case lc.Type_Unexposed: - ctx.logf(decl, "baseClass %s: with unexposed type, skipped", clang.String(decl)) *feats |= featExplicitIgnore return nil } @@ -493,7 +508,7 @@ func loadOutsideMethod(ctx *pkgCtx, outsideDecl clang.Cursor) { if m, ok := ctx.fns[fnUSR]; ok { m.decl = outsideDecl } else { - ctx.logf(outsideDecl, "[WARN] method undeclared - %s", cNameOf(outsideDecl)) + ctx.ignoref(featExplicitIgnore, outsideDecl, "[WARN] method undeclared - %s", cNameOf(outsideDecl)) } } diff --git a/cl/compile.go b/cl/compile.go index c38a94e1..1e8093dd 100644 --- a/cl/compile.go +++ b/cl/compile.go @@ -170,6 +170,10 @@ type Config struct { // for all header files. GoFileOf func(headerFile string) (string, bool) + // FailFast specifies whether to fail fast on errors (optional). If FailFast is non-zero, + // llcppg will stop after printing errors `FailFast` times. + FailFast int + // LoadLibcPubFile specifies whether to load the pubFile for libc package (optional). LoadLibcPubFile bool @@ -233,7 +237,7 @@ func NewPackage(pkgPath, pkgName string, files []Source, conf *Config) (ret Pack typdecls := make(map[string]typDecl) ctx := &pkgCtx{ overloads: make(map[string]*overloads), pkg: pkg, cb: pkg.CB(), - llgo: llgo, fset: pkg.Fset, lang: conf.Language, + llgo: llgo, fset: pkg.Fset, lang: conf.Language, failFast: conf.FailFast, keepDoc: !conf.DontKeepDoc, stdRecvName: conf.UseStdRecvName, cflags: conf.CFlags, wrapFileHeader: conf.WrapFileHeader, typeAbbr: conf.TypeAbbr, typeAbbrSuffix: conf.TypeAbbrSuffix, diff --git a/cl/ctx.go b/cl/ctx.go index 15c9a645..78393fb2 100644 --- a/cl/ctx.go +++ b/cl/ctx.go @@ -22,6 +22,7 @@ import ( "go/token" "go/types" "log" + "os" "sort" "github.com/goplus/gogen" @@ -153,7 +154,8 @@ type pkgCtx struct { compiles []compileUnit pubs []Entry - anonSeq int + failFast int + anonSeq int stdRecvName bool keepDoc bool @@ -162,6 +164,10 @@ type pkgCtx struct { func (p *pkgCtx) ignoref(feats int, decl clang.Cursor, format string, args ...any) { if feats&featQuietIgnore == 0 || debugQuietIgnore { p.logf(decl, format, args...) + p.failFast-- + if p.failFast == 0 { + os.Exit(1) + } } } @@ -253,27 +259,36 @@ func (p *pkgCtx) addType(cName string, decl clang.Cursor, typNamed *types.Named) } func (p *pkgCtx) ignoreType(cName string, feats int) { - if _, ok := p.types[cName]; !ok { - p.types[cName] = typeObj{nil, feats} + if o, ok := p.types[cName]; ok { + if o.TypeName != nil && o.Pkg() == p.pkg.Types { // don't ignore external type + o.feats |= feats + p.types[cName] = o + } + return } + p.types[cName] = typeObj{nil, feats} } -func (p *pkgCtx) isTypeIgnored(cName string) bool { +func (p *pkgCtx) isConfTypeIgnored(cName string) bool { return contains(cName, p.typeIgnores) } -func (p *pkgCtx) typeObj(cName string) (*types.TypeName, bool) { - if o, ok := p.types[cName]; ok && o.feats&featQuietIgnore == 0 { - return o.TypeName, true +func (p *pkgCtx) nsFeats(ns string) int { + if ns != "" { + if o, ok := p.types[ns]; ok { + return o.feats + } } - return nil, false + return 0 } -func (p *pkgCtx) typeOf(cName string) (types.Type, bool) { - if o, ok := p.types[cName]; ok && o.feats&featQuietIgnore == 0 { - return o.Type(), true +func (p *pkgCtx) getTypeObj(cName string, feats *int) (ret *types.TypeName, ok bool) { + o, ok := p.types[cName] + if ok { + ret, ok = o.TypeName, o.feats&featAllIgnore == 0 + *feats |= o.feats } - return nil, false + return } func (p *pkgCtx) isMacroIgnored(cName string) bool { diff --git a/cl/func.go b/cl/func.go index 6703c048..28e75a9e 100644 --- a/cl/func.go +++ b/cl/func.go @@ -77,7 +77,7 @@ func compileFuncOrMethod(ctx *pkgCtx, obj *funcObj, this *classCtx) { manglingName := clang.Mangling(fn) if manglingName == "" { - ctx.logf(fn, "func %s: no mangled symbol, skipped", cName) + ctx.ignoref(featExplicitIgnore, fn, "func %s: no mangled symbol, ignored", cName) return } @@ -100,7 +100,7 @@ func compileFuncOrMethod(ctx *pkgCtx, obj *funcObj, this *classCtx) { } manglingName = wrapInlineFunc(ctx, manglingName, fn, this) } else if _, ok := ctx.nameLookup(manglingName); !ok { - ctx.logf(fn, "func %s: symbol not found in lib files, skipped", cName) + ctx.ignoref(featExplicitIgnore, fn, "func %s: symbol not found in lib files, ignored", cName) return } diff --git a/cl/name.go b/cl/name.go index bc97db39..0c4d93ef 100644 --- a/cl/name.go +++ b/cl/name.go @@ -71,7 +71,7 @@ func cNameOf(decl clang.Cursor) string { cName := cBaseName(decl) for { decl = decl.SemanticParent() - if decl.Kind == lc.Cursor_TranslationUnit { + if decl.IsNull() != 0 || decl.Kind == lc.Cursor_TranslationUnit { break } cName = cBaseName(decl) + "::" + cName diff --git a/cl/type.go b/cl/type.go index 518410d9..6c9ebf49 100644 --- a/cl/type.go +++ b/cl/type.go @@ -129,26 +129,15 @@ func toTypeEx(ctx *pkgCtx, pkg *types.Package, typ lc.Type, flags int, feats *in case lc.Type_FunctionProto: *feats |= featHasCallback return toFuncType(ctx, pkg, typ, feats) - case lc.Type_Enum: + case lc.Type_Enum, lc.Type_Record, lc.Type_Typedef: cName := cTypeName(typ) - if t, ok := ctx.typeOf(cName); ok { - return t - } - case lc.Type_Record, lc.Type_Typedef: - cName := cTypeName(typ) - if o, ok := ctx.types[cName]; ok { - if o.feats != 0 { - *feats |= o.feats - if o.feats&featAllIgnore != 0 { - return types.Typ[types.Invalid] // ignored type - } - } + if o, ok := ctx.getTypeObj(cName, feats); ok { return o.Type() } case lc.Type_Elaborated: cName := cTypeName(typ.Named()) - if t, ok := ctx.typeOf(cName); ok { - return t + if o, ok := ctx.getTypeObj(cName, feats); ok { + return o.Type() } case lc.Type_IncompleteArray, lc.Type_VariableArray: // T[] (and VLAs) have no known extent, so they behave like T*. Clear @@ -170,7 +159,7 @@ func toTypeEx(ctx *pkgCtx, pkg *types.Package, typ lc.Type, flags int, feats *in } return types.NewArray(elem, int64(typ.ArraySize())) case lc.Type_Unexposed: - name := clang.String(typ) + name := clang.String(typ) // maybe typeParams if t, ok := scope.lookupType(name); ok { return t } @@ -179,10 +168,8 @@ func toTypeEx(ctx *pkgCtx, pkg *types.Package, typ lc.Type, flags int, feats *in case lc.Type_BlockPointer: *feats |= featQuietIgnore // will always be ignored return types.Typ[types.Invalid] - default: - ctx.logtf(typ, "toType: unknown kind - %v", typ.Kind) } - ctx.logtf(typ, "toType: unsupported type - %s", clang.String(typ)) + ctx.logtf(typ, "toType: unsupported type - %s (%d: %s)", cTypeName(typ), typ.Kind, clang.String(typ)) *feats |= featExplicitIgnore return types.Typ[types.Invalid] } diff --git a/cl/typedef.go b/cl/typedef.go index 03d0a5d1..e13a6f4d 100644 --- a/cl/typedef.go +++ b/cl/typedef.go @@ -35,22 +35,24 @@ func loadTypedef(ctx *pkgCtx, decl clang.Cursor, scope *scopeCtx) { var cName = cNameOf(decl) var goName = ctx.typeName(cName, true) if cNewName, ok := ctx.typeAlias[cName]; ok { - if t, ok := ctx.typeOf(cNewName); ok { - tunder = t + if o, ok := ctx.getTypeObj(cNewName, &feats); ok { + tunder = o.Type() goto lzFind } - ctx.logf(decl, "typedef %s: alias to %s but not found, skipped", cName, cNewName) + ctx.ignoref(featExplicitIgnore, decl, "typedef %s: alias to %s but not found, ignored", cName, cNewName) + ctx.ignoreType(cName, featExplicitIgnore) return } - if ctx.isTypeIgnored(cName) { + if ctx.isConfTypeIgnored(cName) { ctx.ignoref(featQuietIgnore, decl, "typedef %s: ignored by config", cName) ctx.ignoreType(cName, featQuietIgnore) return } if decl.Kind == lc.Cursor_TypeAliasTemplateDecl { - ctx.logf(decl, "typedef %s: template type alias, skipped", cName) + ctx.ignoref(featExplicitIgnore, decl, "typedef %s: template type alias, ignored", cName) + ctx.ignoreType(cName, featExplicitIgnore) return } @@ -66,7 +68,7 @@ func loadTypedef(ctx *pkgCtx, decl clang.Cursor, scope *scopeCtx) { return } if feats&featExplicitIgnore != 0 { - ctx.logf(decl, "typedef %s: unsupported underlying type, skipped", cName) + ctx.ignoref(featExplicitIgnore, decl, "typedef %s: unsupported underlying type, ignored", cName) ctx.ignoreType(cName, featExplicitIgnore) return } diff --git a/cl/var.go b/cl/var.go index 0ac8eb81..6561106a 100644 --- a/cl/var.go +++ b/cl/var.go @@ -30,15 +30,20 @@ func loadVar(ctx *pkgCtx, decl clang.Cursor) { } func compileVar(ctx *pkgCtx, decl clang.Cursor) { - cName := cNameOf(decl) + ns := cNS(decl) + cName := cNameWithNS(clang.String(decl), ns) + if feats := ctx.nsFeats(ns); feats&featAllIgnore != 0 { + ctx.ignoref(feats, decl, "var %s: parent is ignored", cName) + return + } if varHasInitExpr(ctx, decl) { - ctx.logf(decl, "var %s: has initialized expression, skipped", cName) + ctx.ignoref(featQuietIgnore, decl, "var %s: has initialized expression, ignored", cName) return } manglingName := clang.Mangling(decl) if _, ok := ctx.nameLookup(manglingName); !ok { - ctx.logf(decl, "var %s: symbol not found in lib files, skipped", cName) + ctx.ignoref(featExplicitIgnore, decl, "var %s: symbol not found in lib files, ignored", cName) return } @@ -63,14 +68,16 @@ func compileVar(ctx *pkgCtx, decl clang.Cursor) { defs.New(goNodePos(ctx, decl), typ, goName) } -func varHasInitExpr(_ *pkgCtx, v clang.Cursor) (hasInitExpr bool) { +func varHasInitExpr(ctx *pkgCtx, v clang.Cursor) (hasInitExpr bool) { clang.VisitChildren(v, func(decl, parent clang.Cursor) clang.ChildVisitResult { switch decl.Kind { - case lc.Cursor_InitListExpr, lc.Cursor_CallExpr: + case lc.Cursor_InitListExpr, lc.Cursor_CallExpr, lc.Cursor_DeclRefExpr, + lc.Cursor_UnaryOperator, lc.Cursor_UnexposedExpr: hasInitExpr = true return clang.Break + case lc.Cursor_TypeRef, lc.Cursor_UnaryExpr: default: - // ctx.panicf(decl, "varHasInitExpr: unknown kind - %v", decl.Kind) + ctx.panicf(decl, "varHasInitExpr: unknown kind - %v", decl.Kind) } return clang.Continue }) diff --git a/cmd/llcppg/gen.go b/cmd/llcppg/gen.go index ce1e9cfd..d23b5d41 100644 --- a/cmd/llcppg/gen.go +++ b/cmd/llcppg/gen.go @@ -34,7 +34,7 @@ import ( // Gen generates Go files from the C/C++ header files and llcppg.cfg in the specified // source directory and writes them to the destination directory. It uses the provided // clang index for parsing. -func Gen(destDir, srcDir string, index clang.Index) (err error) { +func Gen(destDir, srcDir string, index clang.Index, verbose bool) (err error) { cfg, err := tool.LoadConf(filepath.Join(srcDir, "llcppg.cfg")) if err != nil { return @@ -45,7 +45,7 @@ func Gen(destDir, srcDir string, index clang.Index) (err error) { } mainPkgName := cfg.Name - if cfg.IgnoreInline { + if !verbose && cfg.IgnoreInline { cl.InlineFuncIgnore = cl.QuietIgnoreInline // quiet ignore inline functions } diff --git a/cmd/llcppg/llcppg.go b/cmd/llcppg/llcppg.go index 1fb5004d..8cc7d014 100644 --- a/cmd/llcppg/llcppg.go +++ b/cmd/llcppg/llcppg.go @@ -58,7 +58,7 @@ func main() { idx := clang.CreateIndex(1, 1) defer idx.Dispose() - err := Gen(destDir, srcDir, idx) + err := Gen(destDir, srcDir, idx, *verbose || *debug) if err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) diff --git a/tool/_testcpp/llvm-22.1.8-support/STLForwardCompat/out.go b/tool/_testcpp/llvm-22.1.8-support/STLForwardCompat/out.go index 337d6ac6..007018fa 100644 --- a/tool/_testcpp/llvm-22.1.8-support/STLForwardCompat/out.go +++ b/tool/_testcpp/llvm-22.1.8-support/STLForwardCompat/out.go @@ -1,9 +1,6 @@ package STLForwardCompat -import ( - "github.com/goplus/lib/c" - _ "unsafe" -) +import "github.com/goplus/lib/c" const LLGoPackage = "link: -L$(llvm-config --libdir) -lLLVM; -lLLVM" @@ -12,49 +9,6 @@ type Identity struct { type IdentityIsTransparent = c.Void type FromRangeT struct { } - -//go:linkname NumbersE C._ZN4llvm7numbers1eE -var NumbersE c.Double - -//go:linkname NumbersEgamma C._ZN4llvm7numbers6egammaE -var NumbersEgamma c.Double - -//go:linkname NumbersLn2 C._ZN4llvm7numbers3ln2E -var NumbersLn2 c.Double - -//go:linkname NumbersLn10 C._ZN4llvm7numbers4ln10E -var NumbersLn10 c.Double - -//go:linkname NumbersLog2e C._ZN4llvm7numbers5log2eE -var NumbersLog2e c.Double - -//go:linkname NumbersLog10e C._ZN4llvm7numbers6log10eE -var NumbersLog10e c.Double - -//go:linkname NumbersPi C._ZN4llvm7numbers2piE -var NumbersPi c.Double - -//go:linkname NumbersInvPi C._ZN4llvm7numbers6inv_piE -var NumbersInvPi c.Double - -//go:linkname NumbersInvSqrtpi C._ZN4llvm7numbers10inv_sqrtpiE -var NumbersInvSqrtpi c.Double - -//go:linkname NumbersSqrt2 C._ZN4llvm7numbers5sqrt2E -var NumbersSqrt2 c.Double - -//go:linkname NumbersInvSqrt2 C._ZN4llvm7numbers9inv_sqrt2E -var NumbersInvSqrt2 c.Double - -//go:linkname NumbersSqrt3 C._ZN4llvm7numbers5sqrt3E -var NumbersSqrt3 c.Double - -//go:linkname NumbersInvSqrt3 C._ZN4llvm7numbers9inv_sqrt3E -var NumbersInvSqrt3 c.Double - -//go:linkname NumbersPhi C._ZN4llvm7numbers3phiE -var NumbersPhi c.Double - type RemoveCvref[T any] struct { } type TypeIdentity[T any] struct { diff --git a/tool/_testcpp/llvm-22.1.8-support/StringRef/out.go b/tool/_testcpp/llvm-22.1.8-support/StringRef/out.go index 307870c8..0cbccb1f 100644 --- a/tool/_testcpp/llvm-22.1.8-support/StringRef/out.go +++ b/tool/_testcpp/llvm-22.1.8-support/StringRef/out.go @@ -56,9 +56,6 @@ func ConsumeUnsignedInteger(Str *StringRef, Radix c.Uint, Result *c.UlongLong) b //go:linkname ConsumeSignedInteger C._ZN4llvm20consumeSignedIntegerERNS_9StringRefEjRx func ConsumeSignedInteger(Str *StringRef, Radix c.Uint, Result *c.LongLong) bool -//go:linkname StringRefNpos C._ZN4llvm9StringRef4nposE -var StringRefNpos c.SizeT - // Compare two strings, ignoring case. // // llgo:link (*StringRef).CompareInsensitive C._ZNK4llvm9StringRef19compare_insensitiveES0_ diff --git a/tool/config.go b/tool/config.go index c46ba697..7647c3dc 100644 --- a/tool/config.go +++ b/tool/config.go @@ -48,10 +48,12 @@ type Config struct { TypeAbbrSuffix []string `json:"TypeAbbrSuffix"` // type abbr suffix to remove, only valid for types that are not present in TypeAbbr TypeAbbr map[string]any `json:"TypeAbbr"` // Go type name to its abbr(s), used in function names Rename map[string]string `json:"Rename"` // renaming of C/C++ names to Go names + TypeAlias map[string]string `json:"TypeAlias"` // C/C++ type name to another C/C++ type name TypeIgnore []string `json:"TypeIgnore"` // C/C++ type names to ignore MacroIgnore []string `json:"MacroIgnore"` // C/C++ macro names to ignore NSIgnore []string `json:"NSIgnore"` // namespaces (Go style names) to ignore Pkgs []string `json:"Pkgs"` // sub-packages to generate, optional + FailFast int `json:"FailFast"` // exit on first N errors, optional IgnoreInline bool `json:"IgnoreInline"` // quietly ignore inline functions GroupSubdir bool `json:"GroupSubdir"` // treats sub-directory files as a single file } diff --git a/tool/gen.go b/tool/gen.go index 085f3280..25f40f74 100644 --- a/tool/gen.go +++ b/tool/gen.go @@ -195,11 +195,13 @@ func (cfg *Config) NewPackage(pkgPath, pkgName, workDir string, index clang.Inde TypeSuffix: cfg.TypeSuffix, TypeAbbr: cfg.TypeAbbr, TypeAbbrSuffix: cfg.TypeAbbrSuffix, + TypeAlias: cfg.TypeAlias, Rename: cfg.Rename, TypeIgnore: cfg.TypeIgnore, MacroIgnore: cfg.MacroIgnore, NSIgnore: cfg.NSIgnore, DefaultGoFile: "llcppg.i.go", + FailFast: cfg.FailFast, UseStdRecvName: true, LoadLibcPubFile: true, NameLookup: nil, diff --git a/tool/gen_test.go b/tool/gen_test.go index 6f52687a..a53331af 100644 --- a/tool/gen_test.go +++ b/tool/gen_test.go @@ -70,8 +70,7 @@ func testGenGo(t *testing.T, pkg *gogen.Package, dir, fname string, exp any) { func testSingleFile(t *testing.T, idx clang.Index, pkgDir, headerDir, headerFile string, conf *tool.Config) { myPkgName := headerFile[len(headerDir) : len(headerFile)-2] - if (myPkgName == "Index" || myPkgName == "DenseMapInfo" || myPkgName == "StringRef") && - runtime.GOOS != "darwin" { + if (myPkgName == "Index" || myPkgName == "DenseMapInfo" || myPkgName == "StringRef" || myPkgName == "STLForwardCompat") && runtime.GOOS != "darwin" { log.Println("==> only test Index on macOS") return }