Repository: goplus/gop
Pull request #996 (2021-12-01 03:55:10)

remove local link of gop, install go+ commands into ./bin of pwd


Patch:
diff --git a/.gitignore b/.gitignore
index 020f65ed..ab2048b1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -43,3 +43,5 @@ go.json
 .idea
 
 cmd/qfmt/qfmt
+
+bin/
diff --git a/cmd/goprun/goprun.go b/cmd/goprun/goprun.go
deleted file mode 100644
index 9aedf1b5..00000000
--- a/cmd/goprun/goprun.go
+++ /dev/null
@@ -1,38 +0,0 @@
-package main
-
-import (
-	"fmt"
-	"log"
-	"os"
-	"os/exec"
-
-	"github.com/goplus/gop/x/gopmod"
-)
-
-func main() {
-	if len(os.Args) < 2 {
-		fmt.Fprint(os.Stderr, "Usage: gorun <file.go> [switch ...]\n\n")
-		return
-	}
-	ctx := gopmod.New("")
-	goProj, err := ctx.OpenProject(0, os.Args[1])
-	if err != nil {
-		fmt.Fprint(os.Stderr, "OpenProject failed:", err)
-		return
-	}
-	goProj.ExecArgs = os.Args[2:]
-	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/gorun/gorun.go b/cmd/gorun/gorun.go
new file mode 100644
index 00000000..d311410a
--- /dev/null
+++ b/cmd/gorun/gorun.go
@@ -0,0 +1,69 @@
+package main
+
+import (
+	"crypto/sha1"
+	"fmt"
+	"log"
+	"os"
+	"os/exec"
+	"path/filepath"
+
+	"github.com/goplus/gop/x/gopmod"
+)
+
+type goFile struct {
+	file string
+	code []byte
+}
+
+func newGoProj(file string, args ...string) *gopmod.Project {
+	code, err := os.ReadFile(file)
+	if err != nil {
+		log.Fatalln(err)
+	}
+	src := &goFile{file: file, code: code}
+	return &gopmod.Project{
+		Source:        src,
+		AutoGenFile:   file,
+		FriendlyFname: filepath.Base(file),
+		ExecArgs:      args,
+	}
+}
+
+func (p *goFile) Fingerp() [20]byte { // source code fingerprint
+	return sha1.Sum(p.code)
+}
+
+func (p *goFile) IsDirty(outFile string, temp bool) bool {
+	return true
+}
+
+func (p *goFile) GenGo(outFile string) error {
+	if p.file != outFile {
+		return os.WriteFile(outFile, p.code, 0666)
+	}
+	return nil
+}
+
+func main() {
+	if len(os.Args) < 2 {
+		fmt.Fprint(os.Stderr, "Usage: gorun <file.go> [switch ...]\n\n")
+		return
+	}
+	goProj := newGoProj(os.Args[1], os.Args[2:]...)
+	ctx := gopmod.New("")
+	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/install.go b/cmd/install.go
index 04f9e103..d4fc1083 100644
--- a/cmd/install.go
+++ b/cmd/install.go
@@ -35,18 +35,12 @@ func getcwd() string {
 	return path
 }
 
-func getGopLocalLink() string {
-	path, _ := os.UserHomeDir()
-	return filepath.Join(path, "gop")
-}
-
 func checkPathExist(path string) bool {
 	_, err := os.Stat(path)
 	return !os.IsNotExist(err)
 }
 
 var gopRoot = getcwd()
-var gopLocalLink = getGopLocalLink()
 var initCommandExecuteEnv = os.Environ()
 var commandExecuteEnv = initCommandExecuteEnv
 
@@ -109,7 +103,7 @@ func findTag(commit string) string {
 
 func getGopBuildFlags() string {
 	buildFlags := fmt.Sprintf("-X github.com/goplus/gop/env.defaultGopRoot=%s", gopRoot)
-	buildFlags = fmt.Sprintf("-X github.com/goplus/gop/env.buildDate=%s", getBuildDateTime())
+	buildFlags += fmt.Sprintf(" -X github.com/goplus/gop/env.buildDate=%s", getBuildDateTime())
 	if commit, ok := getGitInfo(); ok {
 		buildFlags += fmt.Sprintf(" -X github.com/goplus/gop/env.buildCommit=%s", commit)
 		if buildVer := getBuildVer(); buildVer != "" {
@@ -119,19 +113,25 @@ func getGopBuildFlags() string {
 	return buildFlags
 }
 
-func detectGoBinPath() string {
-	goBin, ok := os.LookupEnv("GOBIN")
-	if ok {
-		return goBin
-	}
+/*
+	func detectGoBinPath() string {
+		goBin, ok := os.LookupEnv("GOBIN")
+		if ok {
+			return goBin
+		}
 
-	goPath, ok := os.LookupEnv("GOPATH")
-	if ok {
-		return filepath.Join(goPath, "bin")
+		goPath, ok := os.LookupEnv("GOPATH")
+		if ok {
+			return filepath.Join(goPath, "bin")
+		}
+
+		homeDir, _ := os.UserHomeDir()
+		return filepath.Join(homeDir, "go", "bin")
 	}
+*/
 
-	homeDir, _ := os.UserHomeDir()
-	return filepath.Join(homeDir, "go", "bin")
+func detectGopBinPath() string {
+	return filepath.Join(gopRoot, "bin")
 }
 
 func buildGoplusTools(useGoProxy bool) {
@@ -150,6 +150,9 @@ func buildGoplusTools(useGoProxy bool) {
 		)
 	}
 
+	// Install Go+ binary files under current ./bin directory.
+	commandExecuteEnv = append(commandExecuteEnv, "GOBIN="+detectGopBinPath())
+
 	println("Installing Go+ tools...")
 	os.Chdir(commandsDir)
 	buildOutput, buildErr, err := execCommand("go", "install", "-v", "-ldflags", buildFlags, "./...")
@@ -163,26 +166,12 @@ func buildGoplusTools(useGoProxy bool) {
 	println("Go+ tools installed successfully!")
 }
 
-func linkGoplusToLocal() {
-	fmt.Printf("Linking %s to %s\n", gopRoot, gopLocalLink)
-
-	os.Chdir(gopRoot)
-	if gopLocalLink != gopRoot && !checkPathExist(gopLocalLink) {
-		err := os.Symlink(gopRoot, gopLocalLink)
-		if err != nil {
-			println(err.Error())
-		}
-	}
-
-	fmt.Printf("%s linked to %s successfully!\n", gopRoot, gopLocalLink)
-}
-
 func runTestcases() {
 	println("Start running testcases.")
 	os.Chdir(gopRoot)
 
 	path, _ := os.LookupEnv("PATH")
-	path = fmt.Sprintf("%s:", detectGoBinPath()) + path
+	path = fmt.Sprintf("%s:", detectGopBinPath()) + path
 	commandExecuteEnv = append(commandExecuteEnv, "PATH="+path)
 
 	coverage := "-coverprofile=coverage.txt"
@@ -196,22 +185,12 @@ func runTestcases() {
 	println("End running testcases.")
 }
 
-func localUninstall() {
+func uninstall() {
 	println("Uninstalling Go+ and related tools.")
 
-	goBinPath := detectGoBinPath()
-	filesToRemove := []string{
-		gopLocalLink,
-		filepath.Join(goBinPath, "gop"),
-		filepath.Join(goBinPath, "gopfmt"),
-		filepath.Join(goBinPath, "goptestgo"),
-	}
-
-	for _, file := range filesToRemove {
-		if !checkPathExist(file) {
-			continue
-		}
-		if err := os.Remove(file); err != nil {
+	gopBinPath := detectGopBinPath()
+	if checkPathExist(gopBinPath) {
+		if err := os.RemoveAll(gopBinPath); err != nil {
 			println(err.Error())
 		}
 	}
@@ -234,7 +213,6 @@ func isInChina() bool {
 
 func main() {
 	isInstall := flag.Bool("install", false, "Install Go+")
-	isBuild := flag.Bool("build", false, "Build the Go+")
 	isTest := flag.Bool("test", false, "Run testcases")
 	isUninstall := flag.Bool("uninstall", false, "Uninstall Go+")
 	isGoProxy := flag.Bool("proxy", false, "Set GOPROXY for people in China")
@@ -247,14 +225,9 @@ func main() {
 		useGoProxy = isInChina()
 	}
 	flagActionMap := map[*bool]func(){
-		isInstall: func() {
-			buildGoplusTools(useGoProxy)
-			linkGoplusToLocal()
-			println("Go+ is now installed.")
-		},
+		isInstall:   func() { buildGoplusTools(useGoProxy) },
+		isUninstall: uninstall,
 		isTest:      runTestcases,
-		isBuild:     func() { buildGoplusTools(useGoProxy) },
-		isUninstall: localUninstall,
 	}
 
 	for flag, action := range flagActionMap {
diff --git a/cmd/internal/run/mod.go b/cmd/internal/run/mod.go
index a2b8db7c..d43e5cfe 100644
--- a/cmd/internal/run/mod.go
+++ b/cmd/internal/run/mod.go
@@ -1,12 +1,9 @@
 package run
 
 import (
-	"os"
-	"os/exec"
 	"path/filepath"
 
 	"github.com/goplus/gop/env"
-	"github.com/goplus/gop/x/gopmod"
 	"github.com/qiniu/x/log"
 )
 
@@ -26,34 +23,3 @@ func findGoModDir(dir string) (string, bool) {
 	}
 	return filepath.Dir(modfile), nocachefile
 }
-
-func gopRun(source string, args ...string) {
-	ctx := gopmod.New("")
-	flags := 0
-	if *flagGop {
-		flags = gopmod.FlagGoAsGoPlus
-	}
-	goProj, err := ctx.OpenProject(flags, source)
-	if err != nil {
-		log.Fatalln("OpenProject failed:", err)
-	}
-	goProj.ExecArgs = args
-	goProj.FlagRTOE = *flagRTOE
-	goProj.FlagNRINC = *flagNorun
-	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 47def396..4a4cd838 100644
--- a/cmd/internal/run/run.go
+++ b/cmd/internal/run/run.go
@@ -18,6 +18,8 @@
 package run
 
 import (
+	"crypto/sha1"
+	"encoding/base64"
 	"fmt"
 	"os"
 	"os/exec"
@@ -111,8 +113,9 @@ func runCmd(cmd *base.Command, args []string) {
 	isDir := fi.IsDir()
 
 	var isDirty bool
-	var srcDir, gofile string
+	var srcDir, file, gofile string
 	var pkgs map[string]*ast.Package
+	onExit = nil
 	if isDir {
 		srcDir = src
 		gofile = src + "/gop_autogen.go"
@@ -124,8 +127,37 @@ func runCmd(cmd *base.Command, args []string) {
 			return
 		}
 	} else {
-		gopRun(src, args...)
-		return
+		srcDir, file = filepath.Split(src)
+		modload.Load()
+		isGo := filepath.Ext(file) == ".go"
+		if isGo {
+			hash := sha1.Sum([]byte(src))
+			dir := os.Getenv("HOME") + "/.gop/run"
+			os.MkdirAll(dir, 0755)
+			gofile = dir + "/g" + base64.RawURLEncoding.EncodeToString(hash[:]) + file
+			if *flagRTOE { // remove tempfile on error
+				onExit = func() {
+					os.Remove(gofile)
+				}
+			}
+		} else if hasMultiFiles(srcDir, ".gop") {
+			gofile = filepath.Join(srcDir, "gop_autogen_"+file+".go")
+		} else {
+			gofile = srcDir + "/gop_autogen.go"
+		}
+		isDirty = fileIsDirty(fi, gofile)
+		if isDirty {
+			if isGo {
+				fmt.Println("==> GenGo to", gofile)
+			}
+			if *flagGop {
+				pkgs, err = parser.Parse(fset, src, nil, parserMode)
+			} else {
+				pkgs, err = parser.Parse(fset, src, nil, 0) // TODO: only to check dependencies
+			}
+		} else if *flagNorun {
+			return
+		}
 	}
 	if err != nil {
 		scanner.PrintError(os.Stderr, err)
@@ -164,6 +196,18 @@ func runCmd(cmd *base.Command, args []string) {
 	}
 }
 
+func fileIsDirty(fi os.FileInfo, gofile string) bool {
+	fiDest, err := os.Stat(gofile)
+	if err != nil {
+		return true
+	}
+	return fi.ModTime().After(fiDest.ModTime())
+}
+
+var (
+	onExit func()
+)
+
 func goRun(file string, args []string) {
 	goArgs := make([]string, len(args)+2)
 	goArgs[0] = "run"
@@ -176,6 +220,9 @@ func goRun(file string, args []string) {
 	cmd.Env = os.Environ()
 	err := cmd.Run()
 	if err != nil {
+		if onExit != nil {
+			onExit()
+		}
 		switch e := err.(type) {
 		case *exec.ExitError:
 			os.Exit(e.ExitCode())
@@ -223,4 +270,21 @@ func runGoPkg(src string, args []string, doRun bool) {
 	}
 }
 
+func hasMultiFiles(srcDir string, ext string) bool {
+	var has bool
+	if f, err := os.Open(srcDir); err == nil {
+		defer f.Close()
+		fis, _ := f.ReadDir(-1)
+		for _, fi := range fis {
+			if !fi.IsDir() && filepath.Ext(fi.Name()) == ext {
+				if has {
+					return true
+				}
+				has = true
+			}
+		}
+	}
+	return false
+}
+
 // -----------------------------------------------------------------------------
diff --git a/parser/parser_gop.go b/parser/parser_gop.go
index 9be2ad19..dec7339e 100644
--- a/parser/parser_gop.go
+++ b/parser/parser_gop.go
@@ -176,30 +176,6 @@ func RegisterFileType(ext string, format ast.FileType) {
 
 // -----------------------------------------------------------------------------
 
-func ParseFiles(fset *token.FileSet, files []string, mode Mode) (map[string]*ast.Package, error) {
-	return ParseFSFiles(fset, local, files, mode)
-}
-
-func ParseFSFiles(fset *token.FileSet, fs FileSystem, files []string, mode Mode) (map[string]*ast.Package, error) {
-	ret := map[string]*ast.Package{}
-	for _, file := range files {
-		f, err := ParseFSFile(fset, fs, file, nil, mode)
-		if err != nil {
-			return nil, err
-		}
-		pkgName := f.Name.Name
-		pkg, ok := ret[pkgName]
-		if !ok {
-			pkg = &ast.Package{Name: pkgName, Files: make(map[string]*ast.File)}
-			ret[pkgName] = pkg
-		}
-		pkg.Files[file] = f
-	}
-	return ret, nil
-}
-
-// -----------------------------------------------------------------------------
-
 // ParseFile parses the source code of a single Go+ source file and returns the corresponding ast.File node.
 func ParseFile(fset *token.FileSet, filename string, src interface{}, mode Mode) (f *ast.File, err error) {
 	return ParseFSFile(fset, local, filename, src, mode)
diff --git a/x/gopmod/gocmd.go b/x/gopmod/gocmd.go
index 81c79fbc..9068e16f 100644
--- a/x/gopmod/gocmd.go
+++ b/x/gopmod/gocmd.go
@@ -1,24 +1,7 @@
-/*
- * 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 gopmod
 
 import (
 	"fmt"
-	"os"
 	"os/exec"
 	"path/filepath"
 
@@ -53,19 +36,17 @@ func LoadFlags() string {
 
 type GoCmd struct {
 	*exec.Cmd
-	after func(error) error
-}
-
-func (p GoCmd) IsValid() bool {
-	return p.Cmd != nil
+	after func() error
 }
 
 func (p GoCmd) Run() error {
-	err := p.Cmd.Run()
+	if err := p.Cmd.Run(); err != nil {
+		return err
+	}
 	if p.after != nil {
-		return p.after(err)
+		return p.after()
 	}
-	return err
+	return nil
 }
 
 func goCommand(dir, op string, t *goTarget) (ret GoCmd) {
@@ -79,15 +60,8 @@ func goCommand(dir, op string, t *goTarget) (ret GoCmd) {
 		dir, _ = filepath.Split(t.goFile)
 		exargs[0] = "build"
 		exargs = append(exargs, "-o", t.outFile, t.goFile)
-		ret.after = func(e error) error {
-			if e == nil {
-				e = runCommand(afterDir, t.outFile, proj.ExecArgs...)
-			}
-			if e != nil && t.proj.FlagRTOE { // remove tempfile on error
-				os.Remove(t.goFile)
-				os.Remove(t.outFile)
-			}
-			return e
+		ret.after = func() error {
+			return runCommand(afterDir, t.outFile, proj.ExecArgs...)
 		}
 	} else {
 		exargs = append(exargs, t.goFile)         // 1
diff --git a/x/gopmod/gofile.go b/x/gopmod/gofile.go
deleted file mode 100644
index 1ceb2988..00000000
--- a/x/gopmod/gofile.go
+++ /dev/null
@@ -1,60 +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 gopmod
-
-import (
-	"crypto/sha1"
-	"os"
-	"path/filepath"
-)
-
-// -----------------------------------------------------------------------------
-
-type goFile struct {
-	file string
-	code []byte
-}
-
-func openFromGoFile(file string) (proj *Project, err error) {
-	code, err := os.ReadFile(file)
-	if err != nil {
-		return
-	}
-	proj = &Project{
-		Source:        &goFile{file: file, code: code},
-		AutoGenFile:   file,
-		FriendlyFname: filepath.Base(file),
-	}
-	return
-}
-
-func (p *goFile) Fingerp() [20]byte { // source code fingerprint
-	return sha1.Sum(p.code)
-}
-
-func (p *goFile) IsDirty(outFile string, temp bool) bool {
-	return true
-}
-
-func (p *goFile) GenGo(outFile, modFile string) error {
-	if p.file != outFile {
-		return os.WriteFile(outFile, p.code, 0666)
-	}
-	return nil
-}
-
-// -----------------------------------------------------------------------------
diff --git a/x/gopmod/gopfiles.go b/x/gopmod/gopfiles.go
deleted file mode 100644
index 4868c385..00000000
--- a/x/gopmod/gopfiles.go
+++ /dev/null
@@ -1,111 +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 gopmod
-
-import (
-	"bytes"
-	"crypto/sha1"
-	"log"
-	"os"
-	"path/filepath"
-	"sort"
-
-	"github.com/goplus/gop/cl"
-	"github.com/goplus/gop/parser"
-	"github.com/goplus/gop/token"
-	"github.com/goplus/gox"
-)
-
-// -----------------------------------------------------------------------------
-
-type gopFiles struct {
-	files []string
-}
-
-func (p *Context) openFromGopFiles(files []string) (proj *Project, err error) {
-	proj = &Project{
-		Source:        &gopFiles{files: files},
-		UseDefaultCtx: true,
-	}
-	if len(files) == 1 {
-		proj.FriendlyFname = filepath.Base(files[0])
-	}
-	return
-}
-
-func (p *gopFiles) Fingerp() [20]byte { // source code fingerprint
-	files := make([]string, len(p.files))
-	for i, file := range p.files {
-		files[i], _ = filepath.Abs(file)
-	}
-	sort.Strings(files)
-	var buf bytes.Buffer
-	for _, file := range files {
-		fi, err := os.Stat(file)
-		if err != nil {
-			log.Panicln(err)
-		}
-		buf.WriteString(file)
-		buf.WriteByte('\t')
-		buf.WriteString(fi.ModTime().UTC().String())
-		buf.WriteByte('\n')
-	}
-	return sha1.Sum(buf.Bytes())
-}
-
-func (p *gopFiles) IsDirty(outFile string, temp bool) bool {
-	if fi, err := os.Lstat(outFile); err == nil { // TODO: more strictly
-		return fi.IsDir()
-	}
-	return true
-}
-
-const (
-	parserMode = parser.ParseComments
-)
-
-func (p *gopFiles) GenGo(outFile, modFile string) error {
-	fset := token.NewFileSet()
-	pkgs, err := parser.ParseFiles(fset, p.files, parserMode)
-	if err != nil {
-		return err
-	}
-	if len(pkgs) != 1 {
-		log.Panicln("TODO: mutli packages -", len(pkgs))
-	}
-	mainPkg, ok := pkgs["main"]
-	if !ok {
-		panic("TODO: main package not found")
-	}
-
-	srcDir, _ := filepath.Split(outFile)
-	modDir, _ := filepath.Split(modFile)
-	conf := &cl.Config{
-		Dir: modDir, TargetDir: srcDir, Fset: fset, CacheLoadPkgs: true, PersistLoadPkgs: true}
-	out, err := cl.NewPackage("", mainPkg, conf)
-	if err != nil {
-		return err
-	}
-	err = gox.WriteFile(outFile, out, false)
-	if err != nil {
-		return err
-	}
-	conf.PkgsLoader.Save()
-	return nil
-}
-
-// -----------------------------------------------------------------------------
diff --git a/x/gopmod/gopmod.go b/x/gopmod/gopmod.go
index a73e3f0e..26281fde 100644
--- a/x/gopmod/gopmod.go
+++ b/x/gopmod/gopmod.go
@@ -34,7 +34,7 @@ import (
 type Source interface {
 	Fingerp() [20]byte // source code fingerprint
 	IsDirty(outFile string, defctx bool) bool
-	GenGo(outFile, modFile string) error
+	GenGo(outFile string) error
 }
 
 type Project struct {
@@ -43,9 +43,6 @@ type Project struct {
 	FriendlyFname string // friendly fname of source
 	BuildArgs     []string
 	ExecArgs      []string
-	UseDefaultCtx bool
-	FlagNRINC     bool // do not run if not changed
-	FlagRTOE      bool // remove tempfile on error
 }
 
 type Context struct {
@@ -55,36 +52,28 @@ type Context struct {
 }
 
 func New(dir string) *Context {
+	defctx := false
 	modfile, err := env.GOPMOD(dir)
 	if err != nil {
-		return NewDefault(dir)
-	}
-	return &Context{modfile: modfile, dir: dir}
-}
-
-func NewDefault(dir string) *Context {
-	modfile := env.HOME() + "/.gop/run/go.mod"
-	if _, err := os.Stat(modfile); os.IsNotExist(err) {
-		genDefaultGopMod(modfile)
+		modfile = env.HOME() + "/.gop/cmd/go.mod"
+		if _, err := os.Stat(modfile); os.IsNotExist(err) {
+			genDefaultGopMod(modfile)
+		}
+		defctx = true
 	}
-	return &Context{modfile: modfile, dir: dir, defctx: true}
+	return &Context{modfile: modfile, dir: dir, defctx: defctx}
 }
 
 func (p *Context) GoCommand(op string, src *Project) GoCmd {
-	if src.UseDefaultCtx {
-		p = NewDefault(p.dir)
-	}
 	out := p.out(src)
 	if src.IsDirty(out.goFile, p.defctx) {
 		if p.defctx {
 			dir, _ := filepath.Split(out.goFile)
 			os.Mkdir(dir, 0755)
 		}
-		if err := src.GenGo(out.goFile, p.modfile); err != nil {
+		if err := src.GenGo(out.goFile); err != nil {
 			log.Panicln(err)
 		}
-	} else if src.FlagNRINC { // do not run if not changed
-		return GoCmd{}
 	}
 	return goCommand(p.dir, op, &out)
 }
@@ -106,7 +95,7 @@ func (p *Context) out(src *Project) (ret goTarget) {
 	ret.outFile = dir + "g" + base64.RawURLEncoding.EncodeToString(hash[:])
 	ret.proj = src
 	ret.defctx = p.defctx
-	if ret.defctx || src.AutoGenFile == "" {
+	if ret.defctx {
 		ret.goFile = ret.outFile + fname
 	} else {
 		ret.goFile = src.AutoGenFile
diff --git a/x/gopmod/gopproj.go b/x/gopmod/gopproj.go
deleted file mode 100644
index 5ca47e11..00000000
--- a/x/gopmod/gopproj.go
+++ /dev/null
@@ -1,54 +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 gopmod
-
-import (
-	"os"
-	"path/filepath"
-)
-
-// -----------------------------------------------------------------------------
-
-const (
-	FlagGoAsGoPlus = 1 << iota
-)
-
-func (p *Context) OpenProject(flags int, args ...string) (proj *Project, err error) {
-	if len(args) != 1 {
-		return p.openFromGopFiles(args)
-	}
-	src := args[0]
-	fi, err := os.Stat(src)
-	if err != nil {
-		return
-	}
-	if fi.IsDir() {
-		return p.openFromDir(src)
-	}
-	if (flags&FlagGoAsGoPlus) == 0 && filepath.Ext(src) == ".go" {
-		return openFromGoFile(src)
-	}
-	return p.openFromGopFiles(args)
-}
-
-// -----------------------------------------------------------------------------
-
-func (p *Context) openFromDir(dir string) (proj *Project, err error) {
-	panic("todo")
-}
-
-// -----------------------------------------------------------------------------


Test patch:
diff --git a/parser/parserdir_test.go b/parser/parserdir_test.go
index 9db148cb..4c59d526 100644
--- a/parser/parserdir_test.go
+++ b/parser/parserdir_test.go
@@ -54,10 +54,10 @@ func TestReadSource(t *testing.T) {
 	}
 }
 
-func TestParseFiles(t *testing.T) {
+func TestParseFile(t *testing.T) {
 	fset := token.NewFileSet()
-	if _, err := ParseFiles(fset, []string{"/foo/bar/not-exists"}, PackageClauseOnly); err == nil {
-		t.Fatal("ParseFiles failed: no error?")
+	if _, err := ParseFile(fset, "/foo/bar/not-exists", nil, PackageClauseOnly); err == nil {
+		t.Fatal("ParseFile failed: no error?")
 	}
 }
 
@@ -120,17 +120,6 @@ func TestParseGo(t *testing.T) {
 	}
 }
 
-func TestParseGoFiles(t *testing.T) {
-	fset := token.NewFileSet()
-	pkgs, err := ParseFiles(fset, []string{"./_testdata/functype/functype.go"}, Trace)
-	if err != nil {
-		t.Fatal("TestParseGoFiles: ParseFiles failed -", err)
-	}
-	if len(pkgs) != 1 {
-		t.Fatal("TestParseGoFiles failed: len(pkgs) =", len(pkgs))
-	}
-}
-
 func TestFromTestdata(t *testing.T) {
 	sel := ""
 	dir, err := os.Getwd()