diff --git a/modfile/gop_test.go b/modfile/gop_test.go index a5e2e70..36749c3 100644 --- a/modfile/gop_test.go +++ b/modfile/gop_test.go @@ -171,6 +171,7 @@ xgo 1.1 project github.com/goplus/spx math class .spx Sprite +runner github.com/goplus/spx/v2/cmd/spxrunner v2.0.1 require ( github.com/ajstarks/svgo v0.0.0-20210927141636-6d70534b1098 @@ -182,7 +183,7 @@ func TestGoModCompat2(t *testing.T) { gopmod = goxmodSpx2 ) f, err := modfile.ParseLax("go.mod", []byte(gopmod), nil) - if err != nil || len(f.Syntax.Stmt) != 6 { + if err != nil || len(f.Syntax.Stmt) != 7 { t.Fatal("modfile.ParseLax failed:", f, err) } @@ -191,7 +192,7 @@ func TestGoModCompat2(t *testing.T) { t.Fatal("modfile.ParseLax xgo:", xgo) } - require := f.Syntax.Stmt[5].(*modfile.LineBlock) + require := f.Syntax.Stmt[6].(*modfile.LineBlock) if len(require.Token) != 1 || require.Token[0] != "require" { t.Fatal("modfile.ParseLax require:", require) } @@ -345,6 +346,26 @@ import "\?" math `) doTestParseErr(t, `gop.mod:2: import must declare after a project definition`, ` import math +`) + doTestParseErr(t, `gop.mod:2: runner must declare after a project definition`, ` +runner github.com/goplus/spx/v2/cmd/spxrunner +`) + doTestParseErr(t, `gop.mod:3: usage: runner runnerCmdPkgPath [version]`, ` +project github.com/goplus/spx math +runner +`) + doTestParseErr(t, `gop.mod:3: invalid quoted string: invalid syntax`, ` +project github.com/goplus/spx math +runner "\?" +`) + doTestParseErr(t, `gop.mod:3: invalid syntax`, ` +project github.com/goplus/spx math +runner github.com/goplus/spx/v2/cmd/spxrunner "\?" +`) + doTestParseErr(t, `gop.mod:4: repeated runner statement`, ` +project github.com/goplus/spx math +runner github.com/goplus/spx/v2/cmd/spxrunner +runner github.com/goplus/spx/v2/cmd/spxrunner `) doTestParseErr(t, `gop.mod:2: unknown directive: unknown`, ` unknown .spx diff --git a/modfile/rule.go b/modfile/rule.go index 6063be8..04fb772 100644 --- a/modfile/rule.go +++ b/modfile/rule.go @@ -32,6 +32,18 @@ type Compiler struct { Version string } +// A Runner is the runner statement that specifies a custom runner for the project. +// The runner directive must appear after a project statement and only one runner +// per project is allowed. +// Example: runner github.com/goplus/spx/v2/cmd/spxrunner +// Example with version: runner github.com/goplus/spx/v2/cmd/spxrunner v2.0.1 +// The optional version must be written as the second argument, not as path@version. +type Runner struct { + Path string // command package path of the runner + Version string // optional version of the runner command package + Syntax *Line +} + // A File is the parsed, interpreted form of a gox.mod file. type File struct { XGo *XGo @@ -83,6 +95,7 @@ type Project struct { Works []*Class // work class of classfile PkgPaths []string // package paths of classfile and optional inline-imported packages. Import []*Import // auto-imported packages + Runner *Runner // custom runner Syntax *Line } @@ -319,6 +332,34 @@ usage: class [-embed -prefix=Prefix] *.workExt WorkClass [WorkPrototype]`, sw) errorf("usage: import [name] pkgPath") return } + case "runner": + proj := f.proj() + if proj == nil { + errorf("runner must declare after a project definition") + return + } + if proj.Runner != nil { + errorf("repeated runner statement") + return + } + if len(args) < 1 { + errorf("usage: runner runnerCmdPkgPath [version]") + return + } + runnerPath, err := parsePkgPath(&args[0]) + if err != nil { + wrapError(err) + return + } + runnerVer := "" + if len(args) > 1 { + runnerVer, err = parseString(&args[1]) + if err != nil { + wrapError(err) + return + } + } + proj.Runner = &Runner{Path: runnerPath, Version: runnerVer, Syntax: line} default: if strict { errorf("unknown directive: %s", verb)