diff --git a/gopmod/classfile.go b/gopmod/classfile.go index ae15214..b7aaf81 100644 --- a/gopmod/classfile.go +++ b/gopmod/classfile.go @@ -86,8 +86,8 @@ func (p *Module) ImportClasses(importClass ...func(c *Project)) (err error) { for _, c := range opt.Projects { p.importClass(c, impcls) } - for _, r := range opt.Import { - if err = p.importMod(r.ClassfileMod, impcls); err != nil { + for _, classMod := range opt.ClassMods { + if err = p.importMod(classMod, impcls); err != nil { return } } diff --git a/modfile/gop_test.go b/modfile/gop_test.go index 52b5a09..8c05ab3 100644 --- a/modfile/gop_test.go +++ b/modfile/gop_test.go @@ -62,23 +62,6 @@ func lookupClass(ext string) (c *Project, ok bool) { return } -func TestUpdateLine(t *testing.T) { - line := &Line{InBlock: true} - updateLine(line, "foo", "bar") - if len(line.Token) != 1 && line.Token[0] != "bar" { - t.Fatal("updateLine failed:", line.Token) - } -} - -func TestGetWeight(t *testing.T) { - if getWeight(&modfile.LineBlock{Token: []string{"gop"}}) != directiveGop { - t.Fatal("getWeight require failed") - } - if getWeight(&modfile.LineBlock{Token: []string{"unknown"}}) != directiveLineBlock { - t.Fatal("getWeight unknown failed") - } -} - // ----------------------------------------------------------------------------- const gopmodSpx1 = ` @@ -265,34 +248,6 @@ func TestParse2(t *testing.T) { } } -const gopmodUserProj = ` -gop 1.1 - -import github.com/goplus/spx -` - -func TestParseUser(t *testing.T) { - const ( - gopmod = gopmodUserProj - ) - f, err := Parse("github.com/goplus/gop/gop.mod", []byte(gopmod), nil) - if err != nil || len(f.Import) != 1 { - t.Fatal("Parse:", f, err) - return - } - if f.Import[0].ClassfileMod != "github.com/goplus/spx" { - t.Fatal("Parse => Register:", f.Import) - } - f.AddImport("github.com/goplus/spx") - if len(f.Import) != 1 { - t.Fatal("AddRegister not exist?") - } - f.AddImport("github.com/xushiwei/foogop") - if len(f.Import) != 2 { - t.Fatal("AddRegister failed") - } -} - func TestParseErr(t *testing.T) { doTestParseErr(t, `gop.mod:2: unknown directive: module`, ` module foo @@ -309,15 +264,6 @@ gop 1.1 1.2 `) doTestParseErr(t, `gop.mod:2: invalid gop version '1.x': must match format 1.23`, ` gop 1.x -`) - doTestParseErr(t, `gop.mod:2: import directive expects exactly one argument`, ` -register 1 2 3 -`) - doTestParseErr(t, `gop.mod:2: invalid quoted string: invalid syntax`, ` -register "\?" -`) - doTestParseErr(t, `gop.mod:2: malformed module path "-": leading dash`, ` -register - `) doTestParseErr(t, `gop.mod:2: usage: project [.projExt ProjClass] classFilePkgPath ...`, ` project diff --git a/modfile/read.go b/modfile/read.go index 67b9023..ae4f7c3 100644 --- a/modfile/read.go +++ b/modfile/read.go @@ -49,7 +49,6 @@ type Line = modfile.Line // "x" // "y" // ) -// type LineBlock = modfile.LineBlock // An LParen represents the beginning of a parenthesized line block. @@ -59,10 +58,3 @@ type LParen = modfile.LParen // An RParen represents the end of a parenthesized line block. // It is a place to store whole-line (before) comments. type RParen = modfile.RParen - -// ModulePath returns the module path from the gopmod file text. -// If it cannot find a module path, it returns an empty string. -// It is tolerant of unrelated problems in the gop.mod file. -func ModulePath(mod []byte) string { - return modfile.ModulePath(mod) -} diff --git a/modfile/read_test.go b/modfile/read_test.go deleted file mode 100644 index f4254e1..0000000 --- a/modfile/read_test.go +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (c) 2021 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 modfile - -import ( - "testing" -) - -var modulePathTests = []struct { - input []byte - expected string -}{ - {input: []byte("module \"github.com/rsc/vgotest\""), expected: "github.com/rsc/vgotest"}, - {input: []byte("module github.com/rsc/vgotest"), expected: "github.com/rsc/vgotest"}, - {input: []byte("module \"github.com/rsc/vgotest\""), expected: "github.com/rsc/vgotest"}, - {input: []byte("module github.com/rsc/vgotest"), expected: "github.com/rsc/vgotest"}, - {input: []byte("module `github.com/rsc/vgotest`"), expected: "github.com/rsc/vgotest"}, - {input: []byte("module \"github.com/rsc/vgotest/v2\""), expected: "github.com/rsc/vgotest/v2"}, - {input: []byte("module github.com/rsc/vgotest/v2"), expected: "github.com/rsc/vgotest/v2"}, - {input: []byte("module \"gopkg.in/yaml.v2\""), expected: "gopkg.in/yaml.v2"}, - {input: []byte("module gopkg.in/yaml.v2"), expected: "gopkg.in/yaml.v2"}, - {input: []byte("module \"gopkg.in/check.v1\"\n"), expected: "gopkg.in/check.v1"}, - {input: []byte("module \"gopkg.in/check.v1\n\""), expected: ""}, - {input: []byte("module gopkg.in/check.v1\n"), expected: "gopkg.in/check.v1"}, - {input: []byte("module \"gopkg.in/check.v1\"\r\n"), expected: "gopkg.in/check.v1"}, - {input: []byte("module gopkg.in/check.v1\r\n"), expected: "gopkg.in/check.v1"}, - {input: []byte("module \"gopkg.in/check.v1\"\n\n"), expected: "gopkg.in/check.v1"}, - {input: []byte("module gopkg.in/check.v1\n\n"), expected: "gopkg.in/check.v1"}, - {input: []byte("module \n\"gopkg.in/check.v1\"\n\n"), expected: ""}, - {input: []byte("module \ngopkg.in/check.v1\n\n"), expected: ""}, - {input: []byte("module \"gopkg.in/check.v1\"asd"), expected: ""}, - {input: []byte("module \n\"gopkg.in/check.v1\"\n\n"), expected: ""}, - {input: []byte("module \ngopkg.in/check.v1\n\n"), expected: ""}, - {input: []byte("module \"gopkg.in/check.v1\"asd"), expected: ""}, - {input: []byte("module \nmodule a/b/c "), expected: "a/b/c"}, - {input: []byte("module \" \""), expected: " "}, - {input: []byte("module "), expected: ""}, - {input: []byte("module \" a/b/c \""), expected: " a/b/c "}, - {input: []byte("module \"github.com/rsc/vgotest1\" // with a comment"), expected: "github.com/rsc/vgotest1"}, -} - -func TestModulePath(t *testing.T) { - for _, test := range modulePathTests { - t.Run(string(test.input), func(t *testing.T) { - result := ModulePath(test.input) - if result != test.expected { - t.Fatalf("ModulePath(%q): %s, want %s", string(test.input), result, test.expected) - } - }) - } -} diff --git a/modfile/regtest_test.go b/modfile/regtest_test.go index 0735b69..5531073 100644 --- a/modfile/regtest_test.go +++ b/modfile/regtest_test.go @@ -65,7 +65,7 @@ require ( if err != nil { t.Fatal("LoadFromEx:", err) } - if n := len(mod.Opt.Import); n != 2 { + if n := len(mod.Opt.ClassMods); n != 2 { t.Fatal("len(mod.Opt.Import):", n) } } diff --git a/modfile/rule.go b/modfile/rule.go index af7ffc0..295b75f 100644 --- a/modfile/rule.go +++ b/modfile/rule.go @@ -25,14 +25,13 @@ import ( "github.com/qiniu/x/errors" "golang.org/x/mod/modfile" - "golang.org/x/mod/module" ) // A File is the parsed, interpreted form of a gop.mod file. type File struct { - Gop *Gop - Projects []*Project - Import []*Import + Gop *Gop + Projects []*Project + ClassMods []string Syntax *FileSyntax } @@ -49,14 +48,11 @@ func (p *File) proj() *Project { // current project return p.Projects[n-1] } -// A Module is the module statement. -type Module = modfile.Module - // A Gop is the gop statement. type Gop = modfile.Go -// A Import is the import statement. -type Import struct { +// A Register is the //gop:class statement. +type Register struct { ClassfileMod string // module path of classfile Syntax *Line } @@ -90,6 +86,22 @@ type Class struct { Syntax *Line } +func New(gopmod, gopVer string) *File { + gop := &Line{ + Token: []string{"gop", gopVer}, + } + return &File{ + Gop: &Gop{ + Version: gopVer, + Syntax: gop, + }, + Syntax: &FileSyntax{ + Name: gopmod, + Stmt: []Expr{gop}, + }, + } +} + type VersionFixer = modfile.VersionFixer // Parse parses and returns a gop.mod file. @@ -176,25 +188,6 @@ func (f *File) parseVerb(errs *ErrorList, verb string, line *Line, args []string } f.Gop = &Gop{Syntax: line} f.Gop.Version = args[0] - case "import", "register": // register => import - if len(args) != 1 { - errorf("import directive expects exactly one argument") - return - } - s, err := parseString(&args[0]) - if err != nil { - errorf("invalid quoted string: %v", err) - return - } - err = module.CheckPath(s) - if err != nil { - wrapError(err) - return - } - f.Import = append(f.Import, &Import{ - ClassfileMod: s, - Syntax: line, - }) case "project": if len(args) < 1 { errorf("usage: project [.projExt ProjClass] classFilePkgPath ...") @@ -382,101 +375,3 @@ func (p *Error) Summary() string { } // ----------------------------------------------------------------------------- - -const ( - directiveInvalid = iota - directiveModule - directiveGop - directiveProject - directiveClass -) - -const ( - directiveLineBlock = 0x80 + iota - directiveImport -) - -var directiveWeights = map[string]int{ - "module": directiveModule, - "gop": directiveGop, - "import": directiveImport, - "register": directiveImport, // register => import - "project": directiveProject, - "class": directiveClass, -} - -func getWeight(e Expr) int { - if line, ok := e.(*Line); ok { - return directiveWeights[line.Token[0]] - } - if w, ok := directiveWeights[e.(*LineBlock).Token[0]]; ok { - return w - } - return directiveLineBlock -} - -func updateLine(line *Line, tokens ...string) { - if line.InBlock { - tokens = tokens[1:] - } - line.Token = tokens -} - -func addLine(x *FileSyntax, tokens ...string) *Line { - new := &Line{Token: tokens} - w := directiveWeights[tokens[0]] - for i, e := range x.Stmt { - w2 := getWeight(e) - if w <= w2 { - x.Stmt = append(x.Stmt, nil) - copy(x.Stmt[i+1:], x.Stmt[i:]) - x.Stmt[i] = new - return new - } - } - x.Stmt = append(x.Stmt, new) - return new -} - -func (f *File) AddGopStmt(version string) error { - if !modfile.GoVersionRE.MatchString(version) { - return fmt.Errorf("invalid language version string %q", version) - } - if f.Gop == nil { - if f.Syntax == nil { - f.Syntax = new(FileSyntax) - } - f.Gop = &Gop{ - Version: version, - Syntax: addLine(f.Syntax, "gop", version), - } - } else { - f.Gop.Version = version - updateLine(f.Gop.Syntax, "gop", version) - } - return nil -} - -func (f *File) AddImport(modPath string) { - for _, r := range f.Import { - if r.ClassfileMod == modPath { - return - } - } - f.AddNewImport(modPath) -} - -func (f *File) AddNewImport(modPath string) { - line := addLine(f.Syntax, "import", AutoQuote(modPath)) - r := &Import{ - ClassfileMod: modPath, - Syntax: line, - } - f.Import = append(f.Import, r) -} - -func (f *File) Format() ([]byte, error) { - return modfile.Format(f.Syntax), nil -} - -// ----------------------------------------------------------------------------- diff --git a/modfile/rule_test.go b/modfile/rule_test.go index 1380f16..510af72 100644 --- a/modfile/rule_test.go +++ b/modfile/rule_test.go @@ -16,7 +16,6 @@ package modfile import ( - "bytes" "syscall" "testing" ) @@ -83,86 +82,17 @@ func TestFormat(t *testing.T) { } } -func TestMustQuote(t *testing.T) { - if !MustQuote("") { - t.Fatal("MustQuote failed") - } -} - -// ----------------------------------------------------------------------------- - -var addGopTests = []struct { - desc string - in string - version string - out string -}{ - { - `empty_only`, - ``, - `1.1`, - `gop 1.1 - `, - }, -} - -func TestAddGop(t *testing.T) { - for _, tt := range addGopTests { - t.Run(tt.desc, func(t *testing.T) { - testEdit(t, tt.in, tt.out, true, func(f *File) error { - return f.AddGopStmt(tt.version) - }) - }) - } -} - -func TestAddGopErr(t *testing.T) { - f := new(File) - if e := f.AddGopStmt("1.x"); e == nil { - t.Fatal("AddGoStmt:", e) - } - if e := f.AddGopStmt("1.1"); e != nil { - t.Fatal("AddGoStmt failed:", e) - } - if e := f.AddGopStmt("1.2"); e != nil { - t.Fatal("AddGoStmt failed:", e) - } - if n := len(f.Syntax.Stmt); n != 1 { - t.Fatal("AddGoStmt: len(f.Syntax.Stmt) =", n) +func TestForma2t(t *testing.T) { + f := New("/foo/gop.mod", "1.2.0") + if b := string(Format(f.Syntax)); b != "gop 1.2.0\n" { + t.Fatal("Format failed:", b) } } -func testEdit(t *testing.T, in, want string, strict bool, transform func(f *File) error) *File { - t.Helper() - parse := Parse - if !strict { - parse = ParseLax - } - f, err := parse("in", []byte(in), nil) - if err != nil { - t.Fatal(err) - } - g, err := parse("out", []byte(want), nil) - if err != nil { - t.Fatal(err) - } - golden, err := g.Format() - if err != nil { - t.Fatal(err) - } - - if err := transform(f); err != nil { - t.Fatal(err) - } - out, err := f.Format() - if err != nil { - t.Fatal(err) - } - if !bytes.Equal(out, golden) { - t.Errorf("have:\n%s\nwant:\n%s", out, golden) +func TestMustQuote(t *testing.T) { + if !MustQuote("") { + t.Fatal("MustQuote failed") } - - return f } // ----------------------------------------------------------------------------- diff --git a/modload/module.go b/modload/module.go index 8f50a14..0654a98 100644 --- a/modload/module.go +++ b/modload/module.go @@ -129,10 +129,7 @@ func newGoMod(gomod, modPath, goVer string) *gomodfile.File { } func newGopMod(gopmod, gopVer string) *modfile.File { - opt := new(modfile.File) - opt.AddGopStmt(gopVer) - opt.Syntax.Name = gopmod - return opt + return modfile.New(gopmod, gopVer) } // fixVersion returns a modfile.VersionFixer implemented using the Query function. @@ -208,7 +205,7 @@ func LoadFromEx(gomod, gopmod string, readFile func(string) ([]byte, error)) (p func importClassfileFromGoMod(opt *modfile.File, f *gomodfile.File) { for _, r := range f.Require { if isClass(r) { - opt.AddImport(r.Mod.Path) + opt.ClassMods = append(opt.ClassMods, r.Mod.Path) } } } @@ -236,29 +233,26 @@ func (p Module) HasProject() bool { } func hasGopExtended(opt *modfile.File) bool { - return len(opt.Projects) > 0 || len(opt.Import) > 0 + return len(opt.Projects) > 0 } // Save saves all changes of this module. func (p Module) Save() (err error) { - modfile := p.Modfile() - if modfile == "" { + modf := p.Modfile() + if modf == "" { return ErrSaveDefault } data, err := p.Format() if err != nil { return } - err = os.WriteFile(modfile, data, 0644) + err = os.WriteFile(modf, data, 0644) if err != nil { return } if opt := p.Opt; hasGopExtended(opt) { - data, err = opt.Format() - if err != nil { - return - } + data := modfile.Format(opt.Syntax) err = os.WriteFile(opt.Syntax.Name, data, 0644) } return