diff --git a/chore/gogensig/cmptest/test.go b/chore/gogensig/cmptest/test.go index 69cbff8d1..e6db474a3 100644 --- a/chore/gogensig/cmptest/test.go +++ b/chore/gogensig/cmptest/test.go @@ -17,11 +17,13 @@ import ( // The validateFunc is used to validate the generated file, func RunTest(t *testing.T, pkgName string, isCpp bool, symbolEntries []config.SymbolEntry, cppgConf *cppgtypes.Config, originalCode, expectedOutput string, validateFunc func(t *testing.T, pkg *convert.Package)) { - RunTestWithCheckEqual(t, pkgName, isCpp, symbolEntries, cppgConf, originalCode, expectedOutput, validateFunc, func(t *testing.T, expected, content string) { - if strings.TrimSpace(expected) != strings.TrimSpace(content) { - t.Errorf("does not match expected.\nExpected:\n%s\nGot:\n%s", expected, string(content)) - } - }) + RunTestWithCheckEqual(t, pkgName, isCpp, symbolEntries, cppgConf, originalCode, expectedOutput, validateFunc, CheckResult) +} + +func CheckResult(t *testing.T, expected, content string) { + if strings.TrimSpace(expected) != strings.TrimSpace(content) { + t.Errorf("does not match expected.\nExpected:\n%s\nGot:\n%s", expected, string(content)) + } } // RunTestWithCheckEqual initializes a test Go project with local llgo dependency diff --git a/chore/gogensig/convert/basic/basic.go b/chore/gogensig/convert/basic/basic.go index 5e5d83cd7..43d7ec23a 100644 --- a/chore/gogensig/convert/basic/basic.go +++ b/chore/gogensig/convert/basic/basic.go @@ -33,6 +33,7 @@ func ConvertProcesser(cfg *Config) (*processor.DocFileSetProcessor, *convert.Pac DepIncs: astConvert.Pkg.AllDepIncs(), Done: func() { astConvert.WriteLinkFile() + astConvert.WritePubFile() }, }), astConvert.Pkg, nil } diff --git a/chore/gogensig/convert/convert.go b/chore/gogensig/convert/convert.go index db1f4e6b0..83be6591d 100644 --- a/chore/gogensig/convert/convert.go +++ b/chore/gogensig/convert/convert.go @@ -64,6 +64,10 @@ func (p *AstConvert) WriteLinkFile() { p.Pkg.WriteLinkFile() } +func (p *AstConvert) WritePubFile() { + p.Pkg.WritePubFile() +} + func (p *AstConvert) VisitFuncDecl(funcDecl *ast.FuncDecl) { err := p.Pkg.NewFuncDecl(funcDecl) if err != nil { diff --git a/chore/gogensig/convert/convert_test.go b/chore/gogensig/convert/convert_test.go index d0b1cf8fc..d05ff8b6b 100644 --- a/chore/gogensig/convert/convert_test.go +++ b/chore/gogensig/convert/convert_test.go @@ -3,6 +3,7 @@ package convert_test import ( "log" "os" + "path/filepath" "runtime" "strings" "testing" @@ -330,24 +331,34 @@ func TestSkipBuiltinTypedefine(t *testing.T) { cmptest.RunTest(t, "skip", false, []config.SymbolEntry{ {MangleName: "testInt", CppName: "testInt", GoName: "TestInt"}, {MangleName: "testUint", CppName: "testUint", GoName: "TestUint"}, + {MangleName: "testFile", CppName: "testFile", GoName: "TestFile"}, }, &cppgtypes.Config{ - Deps: []string{"github.com/goplus/llgo/chore/gogensig/convert/testdata/stdint"}, + Deps: []string{ + "github.com/goplus/llgo/chore/gogensig/convert/testdata/stdint", + "github.com/goplus/llgo/chore/gogensig/convert/testdata/stdio", + }, }, ` #include +#include void testInt(int8_t a, int16_t b, int32_t c, int64_t d); void testUint(u_int8_t a, u_int16_t b, u_int32_t c, u_int64_t d); + +void testFile(FILE *f); `, `package skip import ( "github.com/goplus/llgo/chore/gogensig/convert/testdata/stdint" + "github.com/goplus/llgo/chore/gogensig/convert/testdata/stdio" _ "unsafe" ) //go:linkname TestInt C.testInt func TestInt(a stdint.Int8_t, b stdint.Int16_t, c stdint.Int32_t, d stdint.Int64_t) //go:linkname TestUint C.testUint func TestUint(a stdint.U_int8_t, b stdint.U_int16_t, c stdint.U_int32_t, d stdint.U_int64_t) +//go:linkname TestFile C.testFile +func TestFile(f *stdio.FILE) `, func(t *testing.T, pkg *convert.Package) { files, err := os.ReadDir(pkg.GetOutputDir()) if err != nil { @@ -358,13 +369,83 @@ func TestUint(a stdint.U_int8_t, b stdint.U_int16_t, c stdint.U_int32_t, d stdin log.Println("Generated file:", file.Name()) for _, headerFile := range needSkipHeaderFiles { if file.Name() == convert.HeaderFileToGo(headerFile) { - t.Fatal("skip file should not be output") + content, err := os.ReadFile(filepath.Join(pkg.GetOutputDir(), file.Name())) + if err != nil { + t.Fatal(err) + } + t.Fatal("skip file should not be output: " + headerFile + "\n" + string(content)) } } } }) } +func TestPubFile(t *testing.T) { + cmptest.RunTest(t, "pub", false, []config.SymbolEntry{ + {MangleName: "func", CppName: "func", GoName: "Func"}, + }, &cppgtypes.Config{ + Include: []string{"temp.h"}, + }, ` +struct point { + int x; + int y; +}; +struct Capital { + int x; + int y; +}; +union data { + float f; + char str[20]; +}; +typedef unsigned int uint_t; +enum color { + RED = 0, +}; +void func(int a, int b); + `, ` +package pub + +import ( + "github.com/goplus/llgo/c" + _ "unsafe" +) + +type Point struct { + X c.Int + Y c.Int +} + +type Capital struct { + X c.Int + Y c.Int +} + +type Data struct { + Str [20]int8 +} +type Uint_t c.Uint +type Color c.Int + +const Color_RED Color = 0 +//go:linkname Func C.func +func Func(a c.Int, b c.Int) + `, func(t *testing.T, pkg *convert.Package) { + bytes, err := os.ReadFile(filepath.Join(pkg.GetOutputDir(), "llcppg.pub")) + if err != nil { + t.Fatal("llcppg.pub not found") + } + expectedPub := ` +Capital +color Color +data Data +point Point +uint_t Uint_t +` + cmptest.CheckResult(t, expectedPub, string(bytes)) + }) +} + // ===========================error func TestNewAstConvert(t *testing.T) { _, err := convert.NewAstConvert(&convert.AstConvertConfig{ diff --git a/chore/gogensig/convert/deps/deps.go b/chore/gogensig/convert/deps/deps.go index 67e7376c9..0a5396782 100644 --- a/chore/gogensig/convert/deps/deps.go +++ b/chore/gogensig/convert/deps/deps.go @@ -5,6 +5,7 @@ import ( "fmt" "os" "path/filepath" + "sort" "strings" "github.com/goplus/llgo/chore/gogensig/config" @@ -52,7 +53,7 @@ func Import(mod *Module, pkgPath string) (p *CPackage, err error) { if err != nil { return nil, err } - pubs, err := readPubFile(filepath.Join(pkgDir, "llcppg.pub")) + pubs, err := ReadPubFile(filepath.Join(pkgDir, "llcppg.pub")) if err != nil { return nil, err } @@ -63,7 +64,7 @@ func Import(mod *Module, pkgPath string) (p *CPackage, err error) { return &CPackage{Package: pkg, Path: pkgPath, Dir: pkgDir, Pubs: pubs, StdIncs: pkgIncs}, nil } -func readPubFile(pubfile string) (ret map[string]string, err error) { +func ReadPubFile(pubfile string) (ret map[string]string, err error) { b, err := os.ReadFile(pubfile) if err != nil { if os.IsNotExist(err) { @@ -93,11 +94,33 @@ func readPubFile(pubfile string) (ret map[string]string, err error) { return } +func WritePubFile(file string, public map[string]string) (err error) { + if len(public) == 0 { + return + } + f, err := os.Create(file) + if err != nil { + return + } + defer f.Close() + ret := make([]string, 0, len(public)) + for name, goName := range public { + if goName == "" { + ret = append(ret, name) + } else { + ret = append(ret, name+" "+goName) + } + } + sort.Strings(ret) + _, err = f.WriteString(strings.Join(ret, "\n")) + return +} + func findStdIncs(pkgDir string) (incs []string, err error) { file := filepath.Join(pkgDir, "llcppg.cfg") cfg, err := config.GetCppgCfgFromPath(file) if err != nil { return nil, err } - return cfg.StdIncludes, nil + return cfg.Include, nil } diff --git a/chore/gogensig/convert/package.go b/chore/gogensig/convert/package.go index 12238acfc..5b6aa941b 100644 --- a/chore/gogensig/convert/package.go +++ b/chore/gogensig/convert/package.go @@ -41,7 +41,9 @@ type Package struct { outputDir string conf *PackageConfig depIncs []string - inCurPkg bool // whether the current file is in the llcppg package not the dependent files + inCurPkg bool // whether the current file is in the llcppg package not the dependent files + public map[string]string // original C name -> public Go name + } type PackageConfig struct { @@ -57,9 +59,10 @@ type PackageConfig struct { // If SetCurFile is not called, all type conversions will be written to this default Go file. func NewPackage(config *PackageConfig) *Package { p := &Package{ - p: gogen.NewPackage(config.PkgPath, config.Name, config.GenConf), - name: config.Name, - conf: config, + p: gogen.NewPackage(config.PkgPath, config.Name, config.GenConf), + name: config.Name, + conf: config, + public: make(map[string]string), } p.outputDir = config.OutputDir @@ -88,7 +91,7 @@ func NewPackage(config *PackageConfig) *Package { func (p *Package) SetCurFile(file string, isHeaderFile bool, inCurPkg bool) error { var fileName string if isHeaderFile { - // headerfile to go filename + // include path to go filename fileName = HeaderFileToGo(file) } else { // package name as the default file @@ -160,7 +163,7 @@ func (p *Package) NewTypeDecl(typeDecl *ast.TypeDecl) error { return nil } // every type name should be public - name, changed, err := p.DeclName(typeDecl.Name.Name) + name, changed, err := p.DeclName(typeDecl.Name.Name, true) if err != nil { return err } @@ -185,7 +188,7 @@ func (p *Package) NewTypeDecl(typeDecl *ast.TypeDecl) error { } func (p *Package) NewTypedefDecl(typedefDecl *ast.TypedefDecl) error { - name, changed, err := p.DeclName(typedefDecl.Name.Name) + name, changed, err := p.DeclName(typedefDecl.Name.Name, true) if err != nil { // for a typedef ,always appear same name like // typedef struct foo { int a; } foo; @@ -247,7 +250,7 @@ func (p *Package) createEnumType(enumName *ast.Ident) (types.Type, string, error var err error var t *gogen.TypeDecl if enumName != nil { - name, changed, err = p.DeclName(enumName.Name) + name, changed, err = p.DeclName(enumName.Name, true) if err != nil { return nil, "", fmt.Errorf("enum type %s already defined", enumName.Name) } @@ -273,7 +276,7 @@ func (p *Package) createEnumItems(items []*ast.EnumItem, enumType types.Type, en } else { constName = item.Name.Name } - name, changed, err := p.DeclName(constName) + name, changed, err := p.DeclName(constName, false) if err != nil { return fmt.Errorf("enum item %s already defined %w", name, err) } @@ -357,19 +360,24 @@ func (p *Package) WriteToBuffer(genFName string) (*bytes.Buffer, error) { // /path/to/foo.h -> foo.go // /path/to/_intptr.h -> SYS_intptr.go - -func HeaderFileToGo(headerFile string) string { - _, fileName := filepath.Split(headerFile) +// for std include header file path +func HeaderFileToGo(incPath string) string { + // _, fileName := filepath.Split(headerFile) + fileName := strings.ReplaceAll(incPath, string(filepath.Separator), "_") ext := filepath.Ext(fileName) if len(ext) > 0 { fileName = strings.TrimSuffix(fileName, ext) } if strings.HasPrefix(fileName, "_") { - fileName = "SYS" + fileName + fileName = "X" + fileName } return fileName + ".go" } +func (p *Package) WritePubFile() error { + return deps.WritePubFile(filepath.Join(p.outputDir, "llcppg.pub"), p.public) +} + func (p *Package) initDepPkgs() { allDepIncs := make([]string, 0) scope := p.p.Types.Scope() @@ -377,8 +385,17 @@ func (p *Package) initDepPkgs() { allDepIncs = append(allDepIncs, dep.StdIncs...) depPkg := p.p.Import(dep.Path) for cName, pubGoName := range dep.Pubs { + if pubGoName == "" { + pubGoName = cName + } if obj := depPkg.TryRef(pubGoName); obj != nil { - if old := scope.Insert(gogen.NewSubst(token.NoPos, p.p.Types, cName, obj)); old != nil { + var preObj types.Object + if pubGoName == cName { + preObj = obj + } else { + preObj = gogen.NewSubst(token.NoPos, p.p.Types, cName, obj) + } + if old := scope.Insert(preObj); old != nil { log.Printf("conflicted name `%v` in %v, previous definition is %v\n", pubGoName, dep.Path, old) } } @@ -390,7 +407,7 @@ func (p *Package) initDepPkgs() { // For a decl name, if it's a current package, remove the prefixed name // For a decl name, it should be unique // todo(zzy): not current converter package file,need not remove prefixed name -func (p *Package) DeclName(name string) (pubName string, changed bool, err error) { +func (p *Package) DeclName(name string, collect bool) (pubName string, changed bool, err error) { originName := name if p.inCurPkg { name = p.cvt.RemovePrefixedName(name) @@ -399,7 +416,15 @@ func (p *Package) DeclName(name string) (pubName string, changed bool, err error if obj := p.p.Types.Scope().Lookup(name); obj != nil { return "", false, fmt.Errorf("type %s already defined,original name is %s", name, originName) } - return name, name != originName, nil + changed = name != originName + if collect && p.inCurPkg { + if changed { + p.public[originName] = name + } else { + p.public[originName] = "" + } + } + return name, changed, nil } // AllDepIncs returns all std include paths of dependent packages diff --git a/chore/gogensig/convert/package_test.go b/chore/gogensig/convert/package_test.go index 767f61990..f708cd622 100644 --- a/chore/gogensig/convert/package_test.go +++ b/chore/gogensig/convert/package_test.go @@ -220,7 +220,8 @@ func TestPackageWrite(t *testing.T) { } } - headerFilePath := "/path/to/mock_header.h" + incPath := "path/to/mock_header.h" + genPath := convert.HeaderFileToGo(incPath) t.Run("OutputToTempDir", func(t *testing.T) { tempDir, err := os.MkdirTemp("", "test_package_write") @@ -232,13 +233,13 @@ func TestPackageWrite(t *testing.T) { pkg := createTestPkg(t, &convert.PackageConfig{ OutputDir: tempDir, }) - pkg.SetCurFile(headerFilePath, true, true) - err = pkg.Write(headerFilePath) + pkg.SetCurFile(incPath, true, true) + err = pkg.Write(incPath) if err != nil { t.Fatalf("Write method failed: %v", err) } - expectedFilePath := filepath.Join(tempDir, "mock_header.go") + expectedFilePath := filepath.Join(tempDir, genPath) verifyGeneratedFile(t, expectedFilePath) }) @@ -260,13 +261,13 @@ func TestPackageWrite(t *testing.T) { pkg := createTestPkg(t, &convert.PackageConfig{ OutputDir: testpkgDir, }) - pkg.SetCurFile(headerFilePath, true, true) - err = pkg.Write(headerFilePath) + pkg.SetCurFile(incPath, true, true) + err = pkg.Write(incPath) if err != nil { t.Fatalf("Write method failed: %v", err) } - expectedFilePath := filepath.Join(testpkgDir, "mock_header.go") + expectedFilePath := filepath.Join(testpkgDir, genPath) verifyGeneratedFile(t, expectedFilePath) }) @@ -274,7 +275,7 @@ func TestPackageWrite(t *testing.T) { pkg := createTestPkg(t, &convert.PackageConfig{ OutputDir: "/nonexistent/directory", }) - err := pkg.Write(headerFilePath) + err := pkg.Write(incPath) if err == nil { t.Fatal("Expected an error for invalid output directory, but got nil") } @@ -298,7 +299,7 @@ func TestPackageWrite(t *testing.T) { t.Fatalf("Failed to change directory permissions: %v", err) } - err = pkg.Write(headerFilePath) + err = pkg.Write(incPath) if err == nil { t.Fatal("Expected an error for invalid output directory, but got nil") } @@ -1690,13 +1691,18 @@ func TestHeaderFileToGo(t *testing.T) { }{ { name: "normal", - input: "/path/to/foo.h", - expected: "foo.go", + input: "sys/dirent.h", + expected: "sys_dirent.go", }, { name: "sys", - input: "/path/to/_intptr.h", - expected: "SYS_intptr.go", + input: "sys/_pthread/_pthread_types.h", + expected: "sys__pthread__pthread_types.go", + }, + { + name: "sys", + input: "_types.h", + expected: "X_types.go", }, } for _, tc := range testCases { diff --git a/chore/gogensig/convert/testdata/stdint/SYS_uint16_t.go b/chore/gogensig/convert/testdata/stdint/X_types__uint16_t.go similarity index 100% rename from chore/gogensig/convert/testdata/stdint/SYS_uint16_t.go rename to chore/gogensig/convert/testdata/stdint/X_types__uint16_t.go diff --git a/chore/gogensig/convert/testdata/stdint/SYS_uint32_t.go b/chore/gogensig/convert/testdata/stdint/X_types__uint32_t.go similarity index 100% rename from chore/gogensig/convert/testdata/stdint/SYS_uint32_t.go rename to chore/gogensig/convert/testdata/stdint/X_types__uint32_t.go diff --git a/chore/gogensig/convert/testdata/stdint/SYS_uint64_t.go b/chore/gogensig/convert/testdata/stdint/X_types__uint64_t.go similarity index 100% rename from chore/gogensig/convert/testdata/stdint/SYS_uint64_t.go rename to chore/gogensig/convert/testdata/stdint/X_types__uint64_t.go diff --git a/chore/gogensig/convert/testdata/stdint/SYS_uint8_t.go b/chore/gogensig/convert/testdata/stdint/X_types__uint8_t.go similarity index 100% rename from chore/gogensig/convert/testdata/stdint/SYS_uint8_t.go rename to chore/gogensig/convert/testdata/stdint/X_types__uint8_t.go diff --git a/chore/gogensig/convert/testdata/stdint/llcppg.cfg b/chore/gogensig/convert/testdata/stdint/llcppg.cfg index 055ef531d..fa514fea0 100644 --- a/chore/gogensig/convert/testdata/stdint/llcppg.cfg +++ b/chore/gogensig/convert/testdata/stdint/llcppg.cfg @@ -1,5 +1,5 @@ { - "stdIncludes":[ + "include":[ "sys/_types/_int8_t.h", "sys/_types/_int16_t.h", "sys/_types/_int32_t.h", diff --git a/chore/gogensig/convert/testdata/stdint/SYS_int16_t.go b/chore/gogensig/convert/testdata/stdint/sys__types__int16_t.go similarity index 100% rename from chore/gogensig/convert/testdata/stdint/SYS_int16_t.go rename to chore/gogensig/convert/testdata/stdint/sys__types__int16_t.go diff --git a/chore/gogensig/convert/testdata/stdint/SYS_int32_t.go b/chore/gogensig/convert/testdata/stdint/sys__types__int32_t.go similarity index 100% rename from chore/gogensig/convert/testdata/stdint/SYS_int32_t.go rename to chore/gogensig/convert/testdata/stdint/sys__types__int32_t.go diff --git a/chore/gogensig/convert/testdata/stdint/SYS_int64_t.go b/chore/gogensig/convert/testdata/stdint/sys__types__int64_t.go similarity index 100% rename from chore/gogensig/convert/testdata/stdint/SYS_int64_t.go rename to chore/gogensig/convert/testdata/stdint/sys__types__int64_t.go diff --git a/chore/gogensig/convert/testdata/stdint/SYS_int8_t.go b/chore/gogensig/convert/testdata/stdint/sys__types__int8_t.go similarity index 100% rename from chore/gogensig/convert/testdata/stdint/SYS_int8_t.go rename to chore/gogensig/convert/testdata/stdint/sys__types__int8_t.go diff --git a/chore/gogensig/convert/testdata/stdint/SYS_u_int16_t.go b/chore/gogensig/convert/testdata/stdint/sys__types__u_int16_t.go similarity index 100% rename from chore/gogensig/convert/testdata/stdint/SYS_u_int16_t.go rename to chore/gogensig/convert/testdata/stdint/sys__types__u_int16_t.go diff --git a/chore/gogensig/convert/testdata/stdint/SYS_u_int32_t.go b/chore/gogensig/convert/testdata/stdint/sys__types__u_int32_t.go similarity index 100% rename from chore/gogensig/convert/testdata/stdint/SYS_u_int32_t.go rename to chore/gogensig/convert/testdata/stdint/sys__types__u_int32_t.go diff --git a/chore/gogensig/convert/testdata/stdint/SYS_u_int64_t.go b/chore/gogensig/convert/testdata/stdint/sys__types__u_int64_t.go similarity index 100% rename from chore/gogensig/convert/testdata/stdint/SYS_u_int64_t.go rename to chore/gogensig/convert/testdata/stdint/sys__types__u_int64_t.go diff --git a/chore/gogensig/convert/testdata/stdint/SYS_u_int8_t.go b/chore/gogensig/convert/testdata/stdint/sys__types__u_int8_t.go similarity index 100% rename from chore/gogensig/convert/testdata/stdint/SYS_u_int8_t.go rename to chore/gogensig/convert/testdata/stdint/sys__types__u_int8_t.go diff --git a/chore/gogensig/convert/testdata/stdio/X_stdio.go b/chore/gogensig/convert/testdata/stdio/X_stdio.go new file mode 100644 index 000000000..57a3ec4f4 --- /dev/null +++ b/chore/gogensig/convert/testdata/stdio/X_stdio.go @@ -0,0 +1,42 @@ +package stdio + +import ( + "unsafe" + + "github.com/goplus/llgo/c" +) + +type Fpos_t c.LongLong + +type X__sbuf struct { + X_base *int8 + X_size c.Int +} + +type X__sFILEX struct { + Unused [8]uint8 +} + +type X__sFILE struct { + X_p *int8 + X_r c.Int + X_w c.Int + X_flags int16 + X_file int16 + X_bf X__sbuf + X_lbfsize c.Int + X_cookie unsafe.Pointer + X_close func(unsafe.Pointer) c.Int + X_read func(unsafe.Pointer, *int8, c.Int) c.Int + X_seek func(unsafe.Pointer, Fpos_t, c.Int) Fpos_t + X_write func(unsafe.Pointer, *int8, c.Int) c.Int + X_ub X__sbuf + X_extra *X__sFILEX + X_ur c.Int + X_ubuf [3]int8 + X_nbuf [1]int8 + X_lb X__sbuf + X_blksize c.Int + X_offset Fpos_t +} +type FILE X__sFILE diff --git a/chore/gogensig/convert/testdata/stdio/llcppg.cfg b/chore/gogensig/convert/testdata/stdio/llcppg.cfg new file mode 100644 index 000000000..b0f4e308a --- /dev/null +++ b/chore/gogensig/convert/testdata/stdio/llcppg.cfg @@ -0,0 +1,5 @@ +{ + "include":[ + "_stdio.h" + ] +} \ No newline at end of file diff --git a/chore/gogensig/convert/testdata/stdio/llcppg.pub b/chore/gogensig/convert/testdata/stdio/llcppg.pub new file mode 100644 index 000000000..635d70a2f --- /dev/null +++ b/chore/gogensig/convert/testdata/stdio/llcppg.pub @@ -0,0 +1,5 @@ +fpos_t Fpos_t +__sbuf X__sbuf +__sFILEX X__sFILEX +__sFILE X__sFILE +FILE \ No newline at end of file diff --git a/chore/llcppg/types/types.go b/chore/llcppg/types/types.go index 94de183f1..f0c7ec483 100644 --- a/chore/llcppg/types/types.go +++ b/chore/llcppg/types/types.go @@ -22,7 +22,6 @@ type Config struct { CFlags string `json:"cflags"` Libs string `json:"libs"` Include []string `json:"include"` - StdIncludes []string `json:"stdIncludes"` Deps []string `json:"deps"` TrimPrefixes []string `json:"trimPrefixes"` Cplusplus bool `json:"cplusplus"`