Repository: goplus/gop
Pull request #1085 (2022-01-05 06:35:36)

gop run


Patch:
diff --git a/cmd/goprun/goprun.go b/cmd/goprun/goprun.go
deleted file mode 100644
index 9a3956db..00000000
--- a/cmd/goprun/goprun.go
+++ /dev/null
@@ -1,43 +0,0 @@
-package main
-
-import (
-	"fmt"
-	"log"
-	"os"
-	"os/exec"
-
-	"github.com/goplus/gop/x/gopproj"
-	"github.com/goplus/gop/x/gopprojs"
-)
-
-func main() {
-	if len(os.Args) < 2 {
-		fmt.Fprint(os.Stderr, "Usage: goprun package [arguments ...]\n\n")
-		return
-	}
-	proj, args, err := gopprojs.ParseOne(os.Args[1:]...)
-	if err != nil {
-		log.Fatalln(err)
-	}
-	var ctx = gopproj.New("")
-	goProj, err := ctx.OpenProject(0, proj)
-	if err != nil {
-		fmt.Fprint(os.Stderr, "OpenProject failed:", err)
-		return
-	}
-	goProj.ExecArgs = args
-	cmd := ctx.GoCommand("run", goProj)
-	cmd.Stdin = os.Stdin
-	cmd.Stdout = os.Stdout
-	cmd.Stderr = os.Stderr
-	cmd.Env = os.Environ()
-	err = cmd.Run()
-	if err != nil {
-		switch e := err.(type) {
-		case *exec.ExitError:
-			os.Exit(e.ExitCode())
-		default:
-			log.Fatalln(err)
-		}
-	}
-}
diff --git a/cmd/internal/run/mod.go b/cmd/internal/run/mod.go
deleted file mode 100644
index 880d0497..00000000
--- a/cmd/internal/run/mod.go
+++ /dev/null
@@ -1,62 +0,0 @@
-package run
-
-import (
-	"os"
-	"os/exec"
-	"path/filepath"
-
-	"github.com/goplus/gop/env"
-	"github.com/goplus/gop/x/gopproj"
-	"github.com/qiniu/x/log"
-)
-
-func findGoModFile(dir string) (modfile string, err error) {
-	modfile, err = env.GOPMOD(dir)
-	if err != nil {
-		modfile = filepath.Join(env.GOPROOT(), "go.mod")
-		return modfile, nil
-	}
-	return
-}
-
-func findGoModDir(dir string) string {
-	modfile, err := findGoModFile(dir)
-	if err != nil {
-		log.Fatalln("findGoModFile:", err)
-	}
-	return filepath.Dir(modfile)
-}
-
-func gopRun(source string, args ...string) {
-	ctx := gopproj.New("")
-	flags := 0
-	if *flagGop {
-		flags = gopproj.FlagGoAsGoPlus
-	}
-	goProj, err := ctx.OpenFiles(flags, source)
-	if err != nil {
-		log.Fatalln("OpenProject failed:", err)
-	}
-	goProj.ExecArgs = args
-	goProj.FlagNRINC = *flagNorun
-	goProj.FlagRTOE = *flagRTOE
-	if goProj.FlagRTOE {
-		goProj.UseDefaultCtx = true
-	}
-	cmd := ctx.GoCommand("run", goProj)
-	if cmd.IsValid() {
-		cmd.Stdin = os.Stdin
-		cmd.Stdout = os.Stdout
-		cmd.Stderr = os.Stderr
-		cmd.Env = os.Environ()
-		err = cmd.Run()
-		if err != nil {
-			switch e := err.(type) {
-			case *exec.ExitError:
-				os.Exit(e.ExitCode())
-			default:
-				log.Fatalln(err)
-			}
-		}
-	}
-}
diff --git a/cmd/internal/run/run.go b/cmd/internal/run/run.go
index 4b42da0a..ba84f31d 100644
--- a/cmd/internal/run/run.go
+++ b/cmd/internal/run/run.go
@@ -21,22 +21,16 @@ import (
 	"fmt"
 	"os"
 	"os/exec"
-	"path/filepath"
 
-	"github.com/qiniu/x/log"
-
-	"github.com/goplus/gop/ast"
 	"github.com/goplus/gop/cl"
 	"github.com/goplus/gop/cmd/internal/base"
-	"github.com/goplus/gop/parser"
-	"github.com/goplus/gop/scanner"
-	"github.com/goplus/gop/token"
+	"github.com/goplus/gop/x/gopproj"
+	"github.com/goplus/gop/x/gopprojs"
 	"github.com/goplus/gox"
+	"github.com/qiniu/x/log"
 )
 
-// -----------------------------------------------------------------------------
-
-// Cmd - gop run
+// gop run
 var Cmd = &base.Command{
 	UsageLine: "gop run [-asm -quiet -debug -nr -gop -prof] package [arguments...]",
 	Short:     "Run a Go+ program",
@@ -54,23 +48,10 @@ var (
 	flagProf    = flag.Bool("prof", false, "do profile and generate profile report")
 )
 
-const (
-	parserMode = parser.ParseComments
-)
-
 func init() {
 	Cmd.Run = runCmd
 }
 
-func saveGoFile(gofile string, pkg *gox.Package) error {
-	dir := filepath.Dir(gofile)
-	err := os.MkdirAll(dir, 0755)
-	if err != nil {
-		return err
-	}
-	return gox.WriteFile(gofile, pkg, false)
-}
-
 func runCmd(cmd *base.Command, args []string) {
 	err := flag.Parse(args)
 	if err != nil {
@@ -79,7 +60,14 @@ func runCmd(cmd *base.Command, args []string) {
 	if flag.NArg() < 1 {
 		cmd.Usage(os.Stderr)
 	}
-	args = flag.Args()[1:]
+	gopRun(flag.Args())
+}
+
+func gopRun(args []string) {
+	proj, args, err := gopprojs.ParseOne(args...)
+	if err != nil {
+		log.Fatalln(err)
+	}
 
 	if *flagQuiet {
 		log.SetOutputLevel(0x7000)
@@ -99,90 +87,36 @@ func runCmd(cmd *base.Command, args []string) {
 		panic("TODO: profile not impl")
 	}
 
-	fset := token.NewFileSet()
-	src, _ := filepath.Abs(flag.Arg(0))
-	fi, err := os.Stat(src)
-	if err != nil {
-		log.Fatalln("input arg check failed:", err)
+	flags := 0
+	if *flagGop {
+		flags = gopproj.FlagGoAsGoPlus
 	}
-	isDir := fi.IsDir()
-
-	var isDirty bool
-	var srcDir, gofile string
-	var pkgs map[string]*ast.Package
-	if isDir {
-		srcDir = src
-		gofile = src + "/gop_autogen.go"
-		isDirty = true // TODO: check if code changed
-		if isDirty {
-			pkgs, err = parser.ParseDir(fset, src, nil, parserMode)
-		} else if *flagNorun {
-			return
-		}
-	} else {
-		gopRun(src, args...)
+	var ctx = gopproj.New("")
+	goProj, err := ctx.OpenProject(flags, proj)
+	if err != nil {
+		fmt.Fprint(os.Stderr, "OpenProject failed:", err)
 		return
 	}
-	if err != nil {
-		scanner.PrintError(os.Stderr, err)
-		os.Exit(10)
+	goProj.ExecArgs = args
+	goProj.FlagNRINC = *flagNorun
+	goProj.FlagRTOE = *flagRTOE
+	if goProj.FlagRTOE {
+		goProj.UseDefaultCtx = true
 	}
-
-	if isDirty {
-		mainPkg, ok := pkgs["main"]
-		if !ok {
-			if len(pkgs) == 0 && isDir { // not a Go+ package, try runGoPkg
-				runGoPkg(src, args, true)
-				return
-			}
-			fmt.Fprintln(os.Stderr, "TODO: not a main package")
-			os.Exit(12)
-		}
-
-		modDir := findGoModDir(srcDir)
-		conf := &cl.Config{WorkingDir: modDir, TargetDir: srcDir, Fset: fset}
-		out, err := cl.NewPackage("", mainPkg, conf)
-		if err != nil {
-			fmt.Fprintln(os.Stderr, err)
-			os.Exit(11)
-		}
-		err = saveGoFile(gofile, out)
-		if err != nil {
-			log.Fatalln("saveGoFile failed:", err)
-		}
-	}
-
-	goRun(gofile, args)
-	if *flagProf {
-		panic("TODO: profile not impl")
-	}
-}
-
-func goRun(file string, args []string) {
-	goArgs := make([]string, len(args)+2)
-	goArgs[0] = "run"
-	goArgs[1] = file
-	copy(goArgs[2:], args)
-	cmd := exec.Command("go", goArgs...)
+	cmd := ctx.GoCommand("run", goProj)
 	cmd.Stdin = os.Stdin
 	cmd.Stdout = os.Stdout
 	cmd.Stderr = os.Stderr
 	cmd.Env = os.Environ()
-	err := cmd.Run()
+	err = cmd.Run()
 	if err != nil {
 		switch e := err.(type) {
 		case *exec.ExitError:
 			os.Exit(e.ExitCode())
 		default:
-			log.Fatalln("go run failed:", err)
+			log.Fatalln(err)
 		}
 	}
 }
 
-func runGoPkg(src string, args []string, doRun bool) {
-	if doRun {
-		goRun(src+"/.", args)
-	}
-}
-
 // -----------------------------------------------------------------------------
diff --git a/x/gopproj/gopproj.go b/x/gopproj/gopproj.go
index c8005717..05ad7102 100644
--- a/x/gopproj/gopproj.go
+++ b/x/gopproj/gopproj.go
@@ -17,13 +17,38 @@
 package gopproj
 
 import (
+	"fmt"
+	"os"
 	"path/filepath"
 
+	"github.com/goplus/gop/x/gengo"
 	"github.com/goplus/gop/x/gopprojs"
 )
 
 // -----------------------------------------------------------------------------
 
+type handleEvent struct {
+	lastErr error
+}
+
+func (p *handleEvent) OnStart(pkgPath string) {
+	fmt.Fprintln(os.Stderr, pkgPath)
+}
+
+func (p *handleEvent) OnInfo(format string, args ...interface{}) {
+	fmt.Fprintf(os.Stderr, format, args...)
+}
+
+func (p *handleEvent) OnErr(stage string, err error) {
+	p.lastErr = fmt.Errorf("%s: %v", stage, err)
+	fmt.Fprintln(os.Stderr, p.lastErr)
+}
+
+func (p *handleEvent) OnEnd() {
+}
+
+// -----------------------------------------------------------------------------
+
 const (
 	FlagGoAsGoPlus = 1 << iota
 )
@@ -52,7 +77,11 @@ func (p *Context) OpenFiles(flags int, args ...string) (proj *Project, err error
 }
 
 func (p *Context) OpenDir(flags int, dir string) (proj *Project, err error) {
-	panic("todo")
+	ev := new(handleEvent)
+	if !gengo.GenGo(gengo.Config{Event: ev}, false, dir) {
+		return nil, ev.lastErr
+	}
+	return p.OpenFiles(0, filepath.Join(dir, "gop_autogen.go"))
 }
 
 func (p *Context) OpenPkgPath(flags int, pkgPath string) (proj *Project, err error) {
diff --git a/x/gopprojs/proj.go b/x/gopprojs/proj.go
index 1144dbb1..92aabbc0 100644
--- a/x/gopprojs/proj.go
+++ b/x/gopprojs/proj.go
@@ -19,7 +19,6 @@ package gopprojs
 import (
 	"errors"
 	"path/filepath"
-	"strings"
 	"syscall"
 )
 
@@ -59,7 +58,7 @@ func ParseOne(args ...string) (proj Proj, next []string, err error) {
 		}
 		return &FilesProj{Files: args[:n]}, args[n:], nil
 	}
-	if strings.HasPrefix(arg, ".") || strings.HasPrefix(arg, "/") {
+	if isLocal(arg) {
 		return &DirProj{Dir: arg}, args[1:], nil
 	}
 	return &PkgPathProj{Path: arg}, args[1:], nil
@@ -73,6 +72,18 @@ func isFile(fname string) bool {
 	return false
 }
 
+func isLocal(ns string) bool {
+	if len(ns) > 0 {
+		switch c := ns[0]; c {
+		case '/', '\\', '.':
+			return true
+		default:
+			return len(ns) >= 2 && ns[1] == ':' && ('A' <= c && c <= 'Z' || 'a' <= c && c <= 'z')
+		}
+	}
+	return false
+}
+
 // -----------------------------------------------------------------------------
 
 func ParseAll(args ...string) (projs []Proj, err error) {


Test patch:
diff --git a/x/gopprojs/proj_test.go b/x/gopprojs/proj_test.go
index f4fae47d..fb3fd033 100644
--- a/x/gopprojs/proj_test.go
+++ b/x/gopprojs/proj_test.go
@@ -20,6 +20,21 @@ import "testing"
 
 // -----------------------------------------------------------------------------
 
+func TestIsLocal(t *testing.T) {
+	if !isLocal(".") || !isLocal("/") {
+		t.Fatal(`isLocal(".") || isLocal("/")`)
+	}
+	if !isLocal("c:/foo") {
+		t.Fatal(`isLocal("c:/foo")`)
+	}
+	if !isLocal("C:/foo") {
+		t.Fatal(`isLocal("C:/foo")`)
+	}
+	if isLocal("") {
+		t.Fatal(`isLocal("")`)
+	}
+}
+
 func TestParseOne(t *testing.T) {
 	proj, next, err := ParseOne("a.go", "b.go", "abc")
 	if err != nil || len(next) != 1 || next[0] != "abc" {