diff --git a/.gitignore b/.gitignore index 8dfd59f..e18d195 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,4 @@ *.out go.work* +.gop/ diff --git a/gopmod/classfile.go b/gopmod/classfile.go index b132bf2..3bd6fca 100644 --- a/gopmod/classfile.go +++ b/gopmod/classfile.go @@ -33,7 +33,7 @@ var ( SpxProject = &Project{ Ext: ".spx", Class: "Game", - Works: []*Class{{Ext: ".spx", Class: "Sprite"}}, + Works: []*modfile.Class{{Ext: ".spx", Class: "Sprite"}}, PkgPaths: []string{"github.com/goplus/spx", "math"}, } TestProject = &Project{ diff --git a/modload/module.go b/modload/module.go index b58e93d..f20d6be 100644 --- a/modload/module.go +++ b/modload/module.go @@ -23,6 +23,7 @@ import ( "strings" "github.com/goplus/mod" + "github.com/goplus/mod/env" "github.com/goplus/mod/modfile" "github.com/qiniu/x/errors" "golang.org/x/mod/module" @@ -54,6 +55,14 @@ func (p Module) Modfile() string { return "" } +func (p Module) workFile() string { + if syn := p.Syntax; syn != nil { + dir, _ := filepath.Split(syn.Name) + return dir + "go.work" + } + return "" +} + // Root returns absolute root path of this module. func (p Module) Root() string { if syn := p.Syntax; syn != nil { @@ -115,6 +124,12 @@ func Create(dir string, modPath, goVer, gopVer string) (p Module, err error) { return Module{}, fmt.Errorf("gop: %s already exists", gopmod) } + if goVer == "" { + goVer = defaultGoVer + } + if gopVer == "" { + gopVer = defaultGopVer + } mod := newGoMod(gomod, modPath, goVer) opt := newGopMod(gopmod, gopVer) return Module{mod, opt}, nil @@ -285,115 +300,47 @@ func (p Module) Save() (err error) { return } -/* -const ( - gopMod = "github.com/goplus/gop" -) - -// UpdateGoMod updates the go.mod file. -func (p Module) UpdateGoMod(env *env.Gop, checkChanged bool) error { - gopmod := p.Modfile() - dir, file := filepath.Split(gopmod) - if file == "go.mod" { - return nil - } - gomod := dir + "go.mod" - if checkChanged && notChanged(gomod, gopmod) { - return nil - } - return p.saveGoMod(gomod, env) -} - -func (p Module) saveGoMod(gomod string, env *env.Gop) error { - gof := p.convToGoMod(env) - data, err := gof.Format() - if err == nil { - err = os.WriteFile(gomod, data, 0644) - } - return err -} - -func (p Module) convToGoMod(env *env.Gop) *gomodfile.File { - copy := p.File.File - copy.Syntax = cloneGoFileSyntax(copy.Syntax) - addRequireIfNotExist(©, gopMod, env.Version) - addReplaceIfNotExist(©, gopMod, "", env.Root, "") - return © -} - -func addRequireIfNotExist(f *gomodfile.File, path, vers string) { - for _, r := range f.Require { - if r.Mod.Path == path { - return - } - } - f.AddNewRequire(path, vers, false) -} - -func addReplaceIfNotExist(f *gomodfile.File, oldPath, oldVers, newPath, newVers string) { - for _, r := range f.Replace { - if r.Old.Path == oldPath && (oldVers == "" || r.Old.Version == oldVers) { - return - } +// SaveWithGopMod adds `require github.com/goplus/gop` and saves all +// changes of this module. +func (p Module) SaveWithGopMod(gop *env.Gop, flags int) (err error) { + gopVer := getGopVer(gop) + p.requireGop(gop, gopVer, flags) + if err = p.Save(); err != nil { + return } - f.AddReplace(oldPath, oldVers, newPath, newVers) -} -func notChanged(target, src string) bool { - fiTarget, err := os.Stat(target) + var work *gomodfile.WorkFile + var workFile = p.workFile() + b, err := os.ReadFile(workFile) if err != nil { - return false - } - fiSrc, err := os.Stat(src) - if err != nil { - return false - } - return fiTarget.ModTime().After(fiSrc.ModTime()) -} - -// ----------------------------------------------------------------------------- - -func cloneGoFileSyntax(syn *modfile.FileSyntax) *modfile.FileSyntax { - stmt := make([]modfile.Expr, 0, len(syn.Stmt)) - for _, e := range syn.Stmt { - if isGopOrDeletedExpr(e) { - continue + if os.IsNotExist(err) { + b = []byte(`go ` + p.Go.Version) + } else { + return } - stmt = append(stmt, cloneExpr(e)) - } - return &modfile.FileSyntax{ - Name: syn.Name, - Comments: syn.Comments, - Stmt: stmt, } -} - -func cloneExpr(e modfile.Expr) modfile.Expr { - if v, ok := e.(*modfile.LineBlock); ok { - copy := *v - return © + var fixed bool + fix := fixVersion(&fixed) + if work, err = gomodfile.ParseWork(workFile, b, fix); err != nil { + return } - return e + work.AddReplace("github.com/goplus/gop", gopVer, gop.Root, "") + return os.WriteFile(workFile, gomodfile.Format(work.Syntax), 0666) } -func isGopOrDeletedExpr(e modfile.Expr) bool { - switch verb := getVerb(e); verb { - case "", "gop", "register", "project", "class": - return true - } - return false +// requireGop adds require for the github.com/goplus/gop module. +func (p Module) requireGop(gop *env.Gop, gopVer string, flags int) { + p.File.AddRequire("github.com/goplus/gop", gopVer) + // TODO: AddRequire "github.com/qiniu/x" if necessary } -func getVerb(e modfile.Expr) string { - if line, ok := e.(*modfile.Line); ok { - if token := line.Token; len(token) > 0 { - return token[0] - } - return "" // deleted line +func getGopVer(gop *env.Gop) string { + ver := gop.Version + if pos := strings.IndexByte(ver, ' '); pos > 0 { // v1.2.0 devel + ver = ver[:pos] } - return e.(*modfile.LineBlock).Token[0] + return ver } -*/ // ----------------------------------------------------------------------------- diff --git a/modload/module_test.go b/modload/module_test.go index 248d84e..40a0da7 100644 --- a/modload/module_test.go +++ b/modload/module_test.go @@ -18,9 +18,11 @@ package modload import ( "encoding/json" + "os" "runtime" "testing" + "github.com/goplus/mod/env" "github.com/goplus/mod/modfile" gomodfile "golang.org/x/mod/modfile" ) @@ -131,3 +133,76 @@ replace github.com/goplus/yap v0.7.2 => ../ } } } + +func TestSaveDefault(t *testing.T) { + if v := Default.workFile(); v != "" { + t.Fatal("Default.workFile:", v) + } + if err := Default.Save(); err != ErrSaveDefault { + t.Fatal("Default.Save:", err) + } +} + +func TestSave(t *testing.T) { + dir := ".gop/_tempdir" + os.RemoveAll(dir) + os.MkdirAll(dir, 0777) + mod, err := Create(dir, "github.com/foo/bar", "", "") + if err != nil { + t.Fatal("Create:", err) + } + if err = mod.AddRequire("github.com/goplus/yap", "v0.5.0", true); err != nil { + t.Fatal("mod.AddRequire:", err) + } + mod.Save() + + mod, err = Load(dir) + if err != nil { + t.Fatal("Load:", err) + } + if err = mod.SaveWithGopMod(&env.Gop{Version: "v1.2.0 devel", Root: "/foo/bar/gop"}, 0); err != nil { + t.Fatal("mod.SaveWithGopMod:", err) + } + if b, err := mod.File.Format(); err != nil { + t.Fatal("Format:", err) + } else if v := string(b); v != `module github.com/foo/bar + +go 1.18 + +require ( + github.com/goplus/yap v0.5.0 //gop:class + github.com/goplus/gop v1.2.0 +) +` { + t.Fatal("SaveWithGopMod:", v) + } + b, err := os.ReadFile(mod.workFile()) + if err != nil { + t.Fatal("read workFile:", err) + } + if v := string(b); v != `go 1.18 + +replace github.com/goplus/gop v1.2.0 => /foo/bar/gop +` { + t.Fatal("workFile:", v) + } + mod.Opt.Projects = append(mod.Opt.Projects, spxProject) + mod.Save() + b, err = os.ReadFile(mod.Opt.Syntax.Name) + if err != nil { + t.Fatal("read gop.mod:", err) + } + if v := string(b); v != `gop 1.2 +` { + t.Fatal("gop.mod:", v) + } +} + +var ( + spxProject = &modfile.Project{ + Ext: ".spx", + Class: "Game", + Works: []*modfile.Class{{Ext: ".spx", Class: "Sprite"}}, + PkgPaths: []string{"github.com/goplus/spx", "math"}, + } +)