diff --git a/README.md b/README.md index e637e25db..ef2a9bbeb 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ [![VSCode](https://img.shields.io/badge/vscode-Go+-teal.svg)](https://github.com/gopcode/vscode-goplus) * **For engineering**: working in the simplest language that can be mastered by children. -* **For STEM education**: studying an engineering language that can be used to work in the future. +* **For STEM education**: studying an engineering language that can be used for work in the future. * **For data science**: communicating with engineers in the same language. ## How to install diff --git a/ast/mod/deps.go b/ast/mod/deps.go new file mode 100644 index 000000000..3be48c817 --- /dev/null +++ b/ast/mod/deps.go @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2022 The GoPlus Authors (goplus.org). All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package mod + +import ( + "strconv" + "strings" + + goast "go/ast" + gotoken "go/token" + + "github.com/goplus/gop/ast" + "github.com/goplus/gop/token" +) + +// ---------------------------------------------------------------------------- + +type Deps struct { + HandlePkg func(pkgPath string) +} + +func (p Deps) Load(pkg *ast.Package, withGopStd bool) { + for _, f := range pkg.Files { + p.LoadFile(f, withGopStd) + } + for _, f := range pkg.GoFiles { + p.LoadGoFile(f) + } +} + +func (p Deps) LoadGoFile(f *goast.File) { + for _, imp := range f.Imports { + path := imp.Path + if path.Kind == gotoken.STRING { + if s, err := strconv.Unquote(path.Value); err == nil { + if s == "C" { + continue + } + p.HandlePkg(s) + } + } + } +} + +func (p Deps) LoadFile(f *ast.File, withGopStd bool) { + for _, imp := range f.Imports { + path := imp.Path + if path.Kind == token.STRING { + if s, err := strconv.Unquote(path.Value); err == nil { + p.gopPkgPath(s, withGopStd) + } + } + } +} + +func (p Deps) gopPkgPath(s string, withGopStd bool) { + if strings.HasPrefix(s, "gop/") { + if !withGopStd { + return + } + s = "github.com/goplus/gop/" + s[4:] + } else if strings.HasPrefix(s, "C") { + if len(s) == 1 { + s = "github.com/goplus/libc" + } else if s[1] == '/' { + s = s[2:] + if strings.IndexByte(s, '/') < 0 { + s = "github.com/goplus/" + s + } + } + } + p.HandlePkg(s) +} + +// ---------------------------------------------------------------------------- diff --git a/cl/builtin.go b/cl/builtin.go index 751afc2a0..0a5d5f156 100644 --- a/cl/builtin.go +++ b/cl/builtin.go @@ -19,7 +19,6 @@ package cl import ( "go/token" "go/types" - "strings" "github.com/goplus/gox" ) @@ -69,29 +68,3 @@ func newBuiltinDefault(pkg gox.PkgImporter, conf *gox.Config) *types.Package { } // ----------------------------------------------------------------------------- - -type gopImporter struct { - gopRoot string - impFrom types.ImporterFrom -} - -func newGopImporter(gopRoot string, imp types.Importer) types.Importer { - if impFrom, ok := imp.(types.ImporterFrom); ok && gopRoot != "" { - return &gopImporter{gopRoot: gopRoot, impFrom: impFrom} - } - return imp -} - -func (p *gopImporter) Import(pkgPath string) (pkg *types.Package, err error) { - const ( - gop = "github.com/goplus/gop" - ) - if strings.HasPrefix(pkgPath, gop) { - if suffix := pkgPath[len(gop):]; suffix == "" || suffix[0] == '/' { - return p.impFrom.ImportFrom(pkgPath, p.gopRoot, 0) - } - } - return p.impFrom.Import(pkgPath) -} - -// ----------------------------------------------------------------------------- diff --git a/cl/builtin_test.go b/cl/builtin_test.go index 2c5b9c6f1..9d64a164b 100644 --- a/cl/builtin_test.go +++ b/cl/builtin_test.go @@ -131,12 +131,6 @@ func lookupClass(ext string) (c *gopmod.Class, ok bool) { return } -func TestImporter(t *testing.T) { - if newGopImporter("", nil) != nil { - t.Fatal("TestImporter failed") - } -} - func TestGetGoFile(t *testing.T) { if f := getGoFile("a_test.gop", true); f != testingGoFile { t.Fatal("TestGetGoFile:", f) diff --git a/cl/compile.go b/cl/compile.go index 099f3ad81..fc2ecb83a 100644 --- a/cl/compile.go +++ b/cl/compile.go @@ -32,7 +32,6 @@ import ( "github.com/goplus/gop/token" "github.com/goplus/gox" "github.com/goplus/gox/cpackages" - "github.com/goplus/gox/packages" "github.com/goplus/mod/modfile" ) @@ -88,9 +87,6 @@ type Config struct { // TargetDir is the directory in which to generate Go files. TargetDir string - // GopRoot specifies the Go+ root directory. - GopRoot string - // C2goBase specifies base of standard c2go packages. // Default is github.com/goplus/. C2goBase string @@ -385,10 +381,6 @@ func NewPackage(pkgPath string, pkg *ast.Package, conf *Config) (p *gox.Package, targetDir = workingDir } fset := conf.Fset - imp := conf.Importer - if imp == nil { - imp = packages.NewImporter(fset, workingDir) - } files := pkg.Files interp := &nodeInterp{ fset: fset, files: files, workingDir: workingDir, @@ -398,7 +390,7 @@ func NewPackage(pkgPath string, pkg *ast.Package, conf *Config) (p *gox.Package, } confGox := &gox.Config{ Fset: fset, - Importer: newGopImporter(conf.GopRoot, imp), + Importer: conf.Importer, LoadNamed: ctx.loadNamed, HandleErr: ctx.handleErr, NodeInterpreter: interp, diff --git a/cl/compile_test.go b/cl/compile_test.go index 3be0f9dca..cb560c52c 100644 --- a/cl/compile_test.go +++ b/cl/compile_test.go @@ -52,7 +52,6 @@ func init() { LookupClass: lookupClass, LookupPub: lookupPub, C2goBase: "github.com/goplus/gop/cl/internal", - GopRoot: gopRootDir, NoFileLine: true, NoAutoGenMain: true, } diff --git a/cl/expr.go b/cl/expr.go index 62680981c..17d8cf221 100644 --- a/cl/expr.go +++ b/cl/expr.go @@ -652,12 +652,14 @@ func compileCompositeLitElts(ctx *blockCtx, elts []ast.Expr, kind int, expected func compileStructLitInKeyVal(ctx *blockCtx, elts []ast.Expr, t *types.Struct, typ types.Type) { for _, elt := range elts { kv := elt.(*ast.KeyValueExpr) - name := kv.Key.(*ast.Ident).Name - idx := lookupField(t, name) + name := kv.Key.(*ast.Ident) + idx := lookupField(t, name.Name) if idx >= 0 { ctx.cb.Val(idx) } else { - log.Panicln("TODO: struct member not found -", name) + src, pos := ctx.LoadExpr(name) + err := newCodeErrorf(&pos, "%s undefined (type %v has no field or method %s)", src, typ, name.Name) + panic(err) } switch expr := kv.Value.(type) { case *ast.LambdaExpr, *ast.LambdaExpr2: diff --git a/cmd/internal/build/build.go b/cmd/internal/build/build.go index e40a81ef9..068dc28cc 100644 --- a/cmd/internal/build/build.go +++ b/cmd/internal/build/build.go @@ -100,17 +100,17 @@ func build(proj gopprojs.Proj, conf *gop.Config, build *gocmd.BuildConfig) { err = gop.BuildPkgPath("", v.Path, conf, build) case *gopprojs.FilesProj: err = gop.BuildFiles(v.Files, conf, build) - if err != nil { - log.Panicln(err) - } default: log.Panicln("`gop build` doesn't support", reflect.TypeOf(v)) } if err == syscall.ENOENT { fmt.Fprintf(os.Stderr, "gop build %v: not found\n", obj) } else if err != nil { - log.Panicln(err) + fmt.Fprintln(os.Stderr, err) + } else { + return } + os.Exit(1) } // ----------------------------------------------------------------------------- diff --git a/cmd/internal/c2go/c2go.go b/cmd/internal/c2go/c2go.go index e7fef5022..f4d388e24 100644 --- a/cmd/internal/c2go/c2go.go +++ b/cmd/internal/c2go/c2go.go @@ -10,7 +10,7 @@ import ( // gop c2go var Cmd = &base.Command{ - UsageLine: "gop " + c2go.ShortUsage, + UsageLine: "gop c" + c2go.ShortUsage[4:], Short: "Run c2go (convert C to Go) tools", } diff --git a/cmd/internal/gengo/go.go b/cmd/internal/gengo/go.go index 473188ba8..ab361183c 100644 --- a/cmd/internal/gengo/go.go +++ b/cmd/internal/gengo/go.go @@ -18,7 +18,9 @@ package gengo import ( + "fmt" "log" + "os" "reflect" "github.com/goplus/gop" @@ -74,7 +76,8 @@ func runCmd(cmd *base.Command, args []string) { log.Panicln("`gop go` doesn't support", reflect.TypeOf(v)) } if err != nil { - log.Panicln(err) + fmt.Fprintln(os.Stderr, err) + os.Exit(1) } } } diff --git a/cmd/internal/gopget/get.go b/cmd/internal/gopget/get.go index e99e359cd..630028be9 100644 --- a/cmd/internal/gopget/get.go +++ b/cmd/internal/gopget/get.go @@ -24,6 +24,7 @@ import ( "github.com/goplus/gop/cmd/internal/base" "github.com/goplus/gop/x/gopenv" + "github.com/goplus/mod/modcache" "github.com/goplus/mod/modfetch" "github.com/goplus/mod/modload" ) @@ -60,29 +61,36 @@ func runCmd(cmd *base.Command, args []string) { } func get(pkgPath string) { + modBase := "" mod, err := modload.Load(".", 0) hasMod := (err != syscall.ENOENT) if hasMod { check(err) check(mod.UpdateGoMod(gopenv.Get(), true)) + modBase = mod.Path() } - modPath, _ := splitPkgPath(pkgPath) - modVer, isClass, err := modfetch.Get(gopenv.Get(), modPath) + + pkgModVer, _, err := modfetch.GetPkg(pkgPath, modBase) check(err) - if hasMod { - if isClass { - mod.AddRegister(modVer.Path) - fmt.Fprintf(os.Stderr, "gop get: registered %s\n", modVer.Path) - } - check(mod.AddRequire(modVer.Path, modVer.Version)) - fmt.Fprintf(os.Stderr, "gop get: added %s %s\n", modVer.Path, modVer.Version) - check(mod.Save()) - check(mod.UpdateGoMod(gopenv.Get(), false)) + if !hasMod { + return } -} -func splitPkgPath(pkgPath string) (modPathWithVer string, pkgPathNoVer string) { - return pkgPath, pkgPath + pkgModRoot, err := modcache.Path(pkgModVer) + check(err) + + pkgMod, err := modload.Load(pkgModRoot, 0) + check(err) + if pkgMod.Classfile != nil { + mod.AddRegister(pkgModVer.Path) + fmt.Fprintf(os.Stderr, "gop get: registered %s\n", pkgModVer.Path) + } + + check(mod.AddRequire(pkgModVer.Path, pkgModVer.Version)) + fmt.Fprintf(os.Stderr, "gop get: added %s %s\n", pkgModVer.Path, pkgModVer.Version) + + check(mod.Save()) + check(mod.UpdateGoMod(gopenv.Get(), false)) } func check(err error) { diff --git a/cmd/internal/install/install.go b/cmd/internal/install/install.go index 885d273ac..bf37a94db 100644 --- a/cmd/internal/install/install.go +++ b/cmd/internal/install/install.go @@ -90,17 +90,17 @@ func install(proj gopprojs.Proj, conf *gop.Config, install *gocmd.InstallConfig) err = gop.InstallPkgPath("", v.Path, conf, install) case *gopprojs.FilesProj: err = gop.InstallFiles(v.Files, conf, install) - if err != nil { - log.Panicln(err) - } default: log.Panicln("`gop install` doesn't support", reflect.TypeOf(v)) } if err == syscall.ENOENT { fmt.Fprintf(os.Stderr, "gop install %v: not found\n", obj) } else if err != nil { - log.Panicln(err) + fmt.Fprintln(os.Stderr, err) + } else { + return } + os.Exit(1) } // ----------------------------------------------------------------------------- diff --git a/cmd/internal/mod/init.go b/cmd/internal/mod/init.go index 7cd1b45de..e134b66c3 100644 --- a/cmd/internal/mod/init.go +++ b/cmd/internal/mod/init.go @@ -17,8 +17,6 @@ package mod import ( - "fmt" - "os" "runtime" "strings" @@ -49,15 +47,13 @@ Run 'gop help mod init' for more information.`) default: fatal("gop mod init: too many arguments") } + modPath := args[0] mod, err := modload.Create(".", modPath, goMainVer(), env.MainVersion) - if err != nil { - fatal(err) - } + check(err) + err = mod.Save() - if err != nil { - fatal(err) - } + check(err) } func goMainVer() string { @@ -70,8 +66,3 @@ func goMainVer() string { } return ver } - -func fatal(msg interface{}) { - fmt.Fprintln(os.Stderr, msg) - os.Exit(1) -} diff --git a/cmd/internal/mod/mod.go b/cmd/internal/mod/mod.go index 095d28cb4..296f7ecd5 100644 --- a/cmd/internal/mod/mod.go +++ b/cmd/internal/mod/mod.go @@ -16,6 +16,10 @@ package mod import ( + "fmt" + "log" + "os" + "github.com/goplus/gop/cmd/internal/base" ) @@ -29,3 +33,14 @@ var Cmd = &base.Command{ cmdTidy, }, } + +func check(err error) { + if err != nil { + log.Panicln(err) + } +} + +func fatal(msg interface{}) { + fmt.Fprintln(os.Stderr, msg) + os.Exit(1) +} diff --git a/cmd/internal/mod/tidy.go b/cmd/internal/mod/tidy.go index 3c82a7696..b73699994 100644 --- a/cmd/internal/mod/tidy.go +++ b/cmd/internal/mod/tidy.go @@ -17,11 +17,18 @@ package mod import ( + "fmt" "log" + "os" + "os/exec" + "syscall" + "github.com/goplus/gop" "github.com/goplus/gop/cmd/internal/base" "github.com/goplus/gop/x/gopenv" - "github.com/goplus/mod/modload" + "github.com/goplus/mod" + "github.com/goplus/mod/gopmod" + "github.com/goplus/mod/modfetch" ) // gop mod tidy @@ -35,13 +42,52 @@ func init() { } func runTidy(cmd *base.Command, args []string) { - mod, err := modload.Load(".", 0) + mod, err := gopmod.Load(".", mod.GopModOnly) + if err != nil { + if err == syscall.ENOENT { + fmt.Fprintln(os.Stderr, "gop.mod not found") + os.Exit(1) + } + log.Panicln(err) + } + + depMods, err := gop.GenDepMods(mod, mod.Root(), true) check(err) - check(mod.Tidy(gopenv.Get())) + + tidy(mod, depMods) } -func check(err error) { - if err != nil { - log.Fatalln(err) +func tidy(mod *gopmod.Module, depMods map[string]struct{}) { + old := mod.DepMods() + for modPath := range old { + if _, ok := depMods[modPath]; !ok { // removed + mod.DropRequire(modPath) + } + } + for modPath := range depMods { + if _, ok := old[modPath]; !ok { // added + if newMod, err := modfetch.Get(modPath); err != nil { + log.Fatalln(err) + } else { + mod.AddRequire(newMod.Path, newMod.Version) + } + } } + + err := mod.Save() + check(err) + + _, _, err = gop.GenGo(mod.Root()+"/...", &gop.Config{DontUpdateGoMod: true}) + check(err) + + err = mod.UpdateGoMod(gopenv.Get(), true) + check(err) + + cmd := exec.Command("go", "mod", "tidy") + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + cmd.Stdin = os.Stdin + cmd.Dir = mod.Root() + err = cmd.Run() + check(err) } diff --git a/cmd/internal/run/run.go b/cmd/internal/run/run.go index a95186866..ff6c8b4a9 100644 --- a/cmd/internal/run/run.go +++ b/cmd/internal/run/run.go @@ -104,17 +104,17 @@ func run(proj gopprojs.Proj, args []string, chDir bool, conf *gop.Config, run *g err = gop.RunPkgPath(v.Path, args, chDir, conf, run) case *gopprojs.FilesProj: err = gop.RunFiles("", v.Files, args, conf, run) - if err != nil { - log.Panicln(err) - } default: log.Panicln("`gop run` doesn't support", reflect.TypeOf(v)) } if err == syscall.ENOENT { fmt.Fprintf(os.Stderr, "gop run %v: not found\n", obj) } else if err != nil { - log.Panicln(err) + fmt.Fprintln(os.Stderr, err) + } else { + return } + os.Exit(1) } // ----------------------------------------------------------------------------- diff --git a/cmd/internal/test/test.go b/cmd/internal/test/test.go index f174ca18e..c6bfb9156 100644 --- a/cmd/internal/test/test.go +++ b/cmd/internal/test/test.go @@ -90,17 +90,17 @@ func test(proj gopprojs.Proj, conf *gop.Config, test *gocmd.TestConfig) { err = gop.TestPkgPath("", v.Path, conf, test) case *gopprojs.FilesProj: err = gop.TestFiles(v.Files, conf, test) - if err != nil { - log.Panicln(err) - } default: log.Panicln("`gop test` doesn't support", reflect.TypeOf(v)) } if err == syscall.ENOENT { fmt.Fprintf(os.Stderr, "gop test %v: not found\n", obj) } else if err != nil { - log.Panicln(err) + fmt.Fprintln(os.Stderr, err) + } else { + return } + os.Exit(1) } // ----------------------------------------------------------------------------- diff --git a/gendeps.go b/gendeps.go new file mode 100644 index 000000000..9cc4fdc10 --- /dev/null +++ b/gendeps.go @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2022 The GoPlus Authors (goplus.org). All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package gop + +import ( + "fmt" + "go/token" + "io/fs" + "os" + "path/filepath" + "strings" + + "github.com/goplus/gop/parser" + "github.com/goplus/mod/gopmod" + "github.com/goplus/mod/modfetch" + + astmod "github.com/goplus/gop/ast/mod" +) + +// ----------------------------------------------------------------------------- + +func GenDepMods(mod *gopmod.Module, dir string, recursively bool) (ret map[string]struct{}, err error) { + modBase := mod.Path() + ret = make(map[string]struct{}) + err = HandleDeps(mod, dir, recursively, func(pkgPath string) { + modPath, _ := modfetch.Split(pkgPath, modBase) + if modPath != "" { + ret[modPath] = struct{}{} + } + }) + return +} + +func HandleDeps(mod *gopmod.Module, dir string, recursively bool, h func(pkgPath string)) (err error) { + g := depsGen{ + deps: astmod.Deps{HandlePkg: h}, + mod: mod, + fset: token.NewFileSet(), + } + if recursively { + err = filepath.WalkDir(dir, func(path string, d fs.DirEntry, err error) error { + if err == nil && d.IsDir() { + if strings.HasPrefix(d.Name(), "_") { // skip _ + return filepath.SkipDir + } + err = g.gen(path) + if err != nil { + fmt.Fprintln(os.Stderr, err) + } + } + return err + }) + } else { + err = g.gen(dir) + } + return +} + +type depsGen struct { + deps astmod.Deps + mod *gopmod.Module + fset *token.FileSet +} + +func (p depsGen) gen(dir string) (err error) { + pkgs, err := parser.ParseDirEx(p.fset, dir, parser.Config{ + IsClass: p.mod.IsClass, + Mode: parser.ImportsOnly, + }) + if err != nil { + return + } + + for _, pkg := range pkgs { + p.deps.Load(pkg, false) + } + return +} + +// ----------------------------------------------------------------------------- diff --git a/gengo.go b/gengo.go index 8d3077f7c..3d58d7410 100644 --- a/gengo.go +++ b/gengo.go @@ -24,8 +24,6 @@ import ( "strings" "syscall" - "github.com/goplus/gop/x/gopenv" - "github.com/goplus/mod/env" "github.com/goplus/mod/gopmod" "github.com/goplus/mod/modcache" "github.com/goplus/mod/modfetch" @@ -107,10 +105,9 @@ func GenGoPkgPath(workDir, pkgPath string, conf *Config, allowExtern bool) (loca pkgPath = pkgPath[:len(pkgPath)-4] } - gop := gopEnv(conf) - mod, err := gopmod.Load(workDir, gop) + mod, err := gopmod.Load(workDir, 0) if err == syscall.ENOENT && allowExtern { - remotePkgPathDo(pkgPath, gop, func(dir string) { + remotePkgPathDo(pkgPath, func(dir string) { os.Chmod(dir, 0755) defer os.Chmod(dir, 0555) localDir = dir @@ -136,9 +133,8 @@ func GenGoPkgPath(workDir, pkgPath string, conf *Config, allowExtern bool) (loca return } -func remotePkgPathDo(pkgPath string, gop *env.Gop, doSth func(dir string), onErr func(e error)) { - modPath, leftPart := splitPkgPath(pkgPath) - modVer, _, err := modfetch.Get(gop, modPath) +func remotePkgPathDo(pkgPath string, doSth func(dir string), onErr func(e error)) { + modVer, leftPart, err := modfetch.GetPkg(pkgPath, "") if err != nil { onErr(err) } else if dir, err := modcache.Path(modVer); err != nil { @@ -148,31 +144,6 @@ func remotePkgPathDo(pkgPath string, gop *env.Gop, doSth func(dir string), onErr } } -func splitPkgPath(pkgPath string) (modPath, leftPart string) { - if strings.HasPrefix(pkgPath, "github.com/") { - parts := strings.SplitN(pkgPath, "/", 4) - if len(parts) > 3 { - leftPart = parts[3] - if pos := strings.IndexByte(leftPart, '@'); pos > 0 { - parts[2] += leftPart[pos:] - leftPart = leftPart[:pos] - } - modPath = strings.Join(parts[:3], "/") - return - } - } - return pkgPath, "" // TODO: -} - -func gopEnv(conf *Config) *env.Gop { - if conf != nil { - if gop := conf.Gop; gop != nil { - return gop - } - } - return gopenv.Get() -} - // ----------------------------------------------------------------------------- func GenGoFiles(autogen string, files []string, conf *Config) (result []string, err error) { diff --git a/go.mod b/go.mod index 6dfe42727..f0ce156cb 100644 --- a/go.mod +++ b/go.mod @@ -3,9 +3,9 @@ module github.com/goplus/gop go 1.16 require ( - github.com/goplus/c2go v0.7.1 + github.com/goplus/c2go v0.7.2 github.com/goplus/gox v1.11.7 - github.com/goplus/libc v0.3.5 - github.com/goplus/mod v0.9.3 + github.com/goplus/libc v0.3.6 + github.com/goplus/mod v0.9.7 github.com/qiniu/x v1.11.5 ) diff --git a/go.sum b/go.sum index 2bb851d5b..39c124430 100644 --- a/go.sum +++ b/go.sum @@ -2,14 +2,14 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/goplus/c2go v0.7.1 h1:F/ZofTnR9hONoIpTHXhNVNX0m1UBD/jDp5xRbROKq+s= -github.com/goplus/c2go v0.7.1/go.mod h1:1om0h0m9wOzUqgxtLvaPOd2B1raurJmZ3ghGP576zZA= +github.com/goplus/c2go v0.7.2 h1:jK4tD99Nej0PjPjBRZRahTftDPue5P+csylca/XjsMo= +github.com/goplus/c2go v0.7.2/go.mod h1:Gd/r7JdrxXDoFKXyP6tsKTM0+j50CGxT0QDJ/5k8hYU= github.com/goplus/gox v1.11.7 h1:YbTrET69TPdZFwT7ESKeDmwq7wpXRb3DUZ474bCC9Og= github.com/goplus/gox v1.11.7/go.mod h1:gu7fuQF8RmWPZUjd+tEJGuV3m/vOEv0bHrct0x/KatM= -github.com/goplus/libc v0.3.5 h1:dWyOxT2Smdz93daPXbgbEr6QuyFClXsu9XMWBZ2LZSM= -github.com/goplus/libc v0.3.5/go.mod h1:Tylp4skmMW4TZsGKpajx1TARMtExegAqRX0bzAEGZW8= -github.com/goplus/mod v0.9.3 h1:kXN2kavdUacqy39Lh/pIdT0zvBTKevb4u96ZbapPbcg= -github.com/goplus/mod v0.9.3/go.mod h1:NHU13OjeNV3ez1f+R9FLJIlIun0KNSuZb0jnmP3N3o8= +github.com/goplus/libc v0.3.6 h1:yqSe2179WwWyETo8vsqxwAgUL4EgjNVjPt68Pk6G/4U= +github.com/goplus/libc v0.3.6/go.mod h1:nyKm7Iir6iI6hQT5WWUG6Jomx2zt/XRAGLLKtLrlICI= +github.com/goplus/mod v0.9.7 h1:dH1XuDlMTkbWgEP1V4Mpz834W5NGwnfGQZ7tSpR5HXU= +github.com/goplus/mod v0.9.7/go.mod h1:NHU13OjeNV3ez1f+R9FLJIlIun0KNSuZb0jnmP3N3o8= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc= diff --git a/imp.go b/imp.go new file mode 100644 index 000000000..3111a6130 --- /dev/null +++ b/imp.go @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2022 The GoPlus Authors (goplus.org). All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package gop + +import ( + "go/token" + "go/types" + "strings" + + "github.com/goplus/gox/packages" + "github.com/goplus/mod/gopmod" + "github.com/goplus/mod/modfetch" +) + +// ----------------------------------------------------------------------------- + +type Importer struct { + impFrom *packages.Importer + mod *gopmod.Module + gopRoot string +} + +func NewImporter(mod *gopmod.Module, fset *token.FileSet, gopRoot string) *Importer { + dir := "" + if mod.IsValid() { + dir = mod.Root() + } + impFrom := packages.NewImporter(fset, dir) + return &Importer{mod: mod, gopRoot: gopRoot, impFrom: impFrom} +} + +func (p *Importer) Import(pkgPath string) (pkg *types.Package, err error) { + const ( + gop = "github.com/goplus/gop" + ) + if strings.HasPrefix(pkgPath, gop) { + if suffix := pkgPath[len(gop):]; suffix == "" || suffix[0] == '/' { + return p.impFrom.ImportFrom(pkgPath, p.gopRoot, 0) + } + } + if mod := p.mod; mod.IsValid() { + if mod.PkgType(pkgPath) == gopmod.PkgtExtern { + ret, modVer, e := mod.LookupExternPkg(pkgPath) + if e != nil { + return nil, e + } + if modVer.Version != "" { + if _, err = modfetch.Get(modVer.String()); err != nil { + return + } + } + return p.impFrom.ImportFrom(pkgPath, ret.Dir, 0) + } + } + return p.impFrom.Import(pkgPath) +} + +// ----------------------------------------------------------------------------- diff --git a/load.go b/load.go index f10b3fe30..e37221ee3 100644 --- a/load.go +++ b/load.go @@ -19,7 +19,6 @@ package gop import ( "errors" "go/token" - "go/types" "io/fs" "path/filepath" "strings" @@ -35,19 +34,18 @@ import ( ) type Config struct { - Gop *env.Gop - Fset *token.FileSet - Filter func(fs.FileInfo) bool - Importer types.Importer + Gop *env.Gop + Fset *token.FileSet + Filter func(fs.FileInfo) bool - UpdateGoMod bool - CheckModChanged bool + DontUpdateGoMod bool + DontCheckModChanged bool } // ----------------------------------------------------------------------------- func loadMod(dir string, gop *env.Gop, conf *Config) (mod *gopmod.Module, err error) { - mod, err = gopmod.Load(dir, gop) + mod, err = gopmod.Load(dir, 0) if err != nil && err != syscall.ENOENT { return } @@ -56,8 +54,8 @@ func loadMod(dir string, gop *env.Gop, conf *Config) (mod *gopmod.Module, err er if err != nil { return } - if conf.UpdateGoMod { - err = mod.UpdateGoMod(gop, conf.CheckModChanged) + if !conf.DontUpdateGoMod { + err = mod.UpdateGoMod(gop, !conf.DontCheckModChanged) } return } @@ -108,9 +106,8 @@ func LoadDir(dir string, conf *Config) (out, test *gox.Package, err error) { var pkgTest *ast.Package var clConf = &cl.Config{ WorkingDir: dir, - GopRoot: gop.Root, Fset: fset, - Importer: conf.Importer, + Importer: NewImporter(mod, fset, gop.Root), LookupClass: mod.LookupClass, LookupPub: lookupPub(mod), } @@ -170,9 +167,8 @@ func LoadFiles(files []string, conf *Config) (out *gox.Package, err error) { } for _, pkg := range pkgs { out, err = cl.NewPackage("", pkg, &cl.Config{ - GopRoot: gop.Root, Fset: fset, - Importer: conf.Importer, + Importer: NewImporter(mod, fset, gop.Root), LookupClass: mod.LookupClass, LookupPub: lookupPub(mod), }) diff --git a/testdata/helloc2go/README.md b/testdata/helloc2go/README.md new file mode 100644 index 000000000..a5bdad347 --- /dev/null +++ b/testdata/helloc2go/README.md @@ -0,0 +1,26 @@ +This is an example to show how Go+ interacts with C. + +```go +import "C" + +C.printf C"Hello, c2go!\n" +C.fprintf C.stderr, C"Hi, %7.1f\n", 3.14 +``` + +Here we use `import "C"` to import libc. It's an abbreviation for `import "C/github.com/goplus/libc"`. It is equivalent to the following code: + +```go +import "C/github.com/goplus/libc" + +C.printf C"Hello, c2go!\n" +C.fprintf C.stderr, C"Hi, %7.1f\n", 3.14 +``` + +In this example we call two C standard functions `printf` and `fprintf`, pass a C variable `stderr` and two C strings in the form of `C"xxx"`. + +The output of this example is as follows: + +``` +Hello, c2go! +Hi, 3.1 +``` diff --git a/testdata/mixgo/README.md b/testdata/mixgo/README.md new file mode 100644 index 000000000..780dbc07f --- /dev/null +++ b/testdata/mixgo/README.md @@ -0,0 +1,33 @@ +This is an example to show how to mix Go/Go+ programming in the same package. + +In this example, we have a Go source file named `a.go`: + +```go +package main + +import "fmt" + +func p(a interface{}) { + sayMix() + fmt.Println("Hello,", a) +} +``` + +And we have a Go+ source file named `b.gop`: + +```go +func sayMix() { + println "Mix Go and Go+" +} + +p "world" +``` + +You can see that Go calls a Go+ function named `sayMix`, and Go+ calls a Go function named `p`. As you are used to in Go programming, this kind of circular reference is allowed. + +The output of this example is as follows: + +``` +Mix Go and Go+ +Hello, world +```