Repository: goplus/gop
Pull request #1221 (2022-05-30 11:28:32)

gop.NewImporter: support import Go+ packages


Patch:
diff --git a/build_install_run.go b/build_install_run.go
index 706ac959..089b7d00 100644
--- a/build_install_run.go
+++ b/build_install_run.go
@@ -27,7 +27,7 @@ import (
 // -----------------------------------------------------------------------------
 
 func InstallDir(dir string, conf *Config, install *gocmd.InstallConfig) (err error) {
-	_, _, err = GenGo(dir, conf)
+	_, _, err = GenGo(dir, conf, false)
 	if err != nil {
 		return
 	}
@@ -74,7 +74,7 @@ func chdir(dir string) string {
 // -----------------------------------------------------------------------------
 
 func BuildDir(dir string, conf *Config, build *gocmd.BuildConfig) (err error) {
-	_, _, err = GenGo(dir, conf)
+	_, _, err = GenGo(dir, conf, false)
 	if err != nil {
 		return
 	}
@@ -112,7 +112,7 @@ func restoreDirAndMod(old string) {
 // -----------------------------------------------------------------------------
 
 func RunDir(dir string, args []string, conf *Config, run *gocmd.RunConfig) (err error) {
-	_, _, err = GenGo(dir, conf)
+	_, _, err = GenGo(dir, conf, false)
 	if err != nil {
 		return
 	}
@@ -146,7 +146,7 @@ func RunFiles(autogen string, files []string, args []string, conf *Config, run *
 // -----------------------------------------------------------------------------
 
 func TestDir(dir string, conf *Config, test *gocmd.TestConfig) (err error) {
-	_, _, err = GenGo(dir, conf)
+	_, _, err = GenGo(dir, conf, true)
 	if err != nil {
 		return
 	}
diff --git a/cmd/internal/gengo/go.go b/cmd/internal/gengo/go.go
index ab361183..b84be5d0 100644
--- a/cmd/internal/gengo/go.go
+++ b/cmd/internal/gengo/go.go
@@ -69,7 +69,7 @@ func runCmd(cmd *base.Command, args []string) {
 	for _, proj := range projs {
 		switch v := proj.(type) {
 		case *gopprojs.DirProj:
-			_, _, err = gop.GenGo(v.Dir, nil)
+			_, _, err = gop.GenGo(v.Dir, nil, true)
 		case *gopprojs.PkgPathProj:
 			_, _, err = gop.GenGoPkgPath("", v.Path, nil, true)
 		default:
diff --git a/cmd/internal/mod/tidy.go b/cmd/internal/mod/tidy.go
index b7369999..bfe5a2a8 100644
--- a/cmd/internal/mod/tidy.go
+++ b/cmd/internal/mod/tidy.go
@@ -77,7 +77,7 @@ func tidy(mod *gopmod.Module, depMods map[string]struct{}) {
 	err := mod.Save()
 	check(err)
 
-	_, _, err = gop.GenGo(mod.Root()+"/...", &gop.Config{DontUpdateGoMod: true})
+	_, _, err = gop.GenGo(mod.Root()+"/...", &gop.Config{DontUpdateGoMod: true}, true)
 	check(err)
 
 	err = mod.UpdateGoMod(gopenv.Get(), true)
diff --git a/gengo.go b/gengo.go
index 3d58d741..badd25c3 100644
--- a/gengo.go
+++ b/gengo.go
@@ -38,22 +38,22 @@ const (
 
 // -----------------------------------------------------------------------------
 
-func GenGo(dir string, conf *Config) (string, bool, error) {
+func GenGo(dir string, conf *Config, genTestPkg bool) (string, bool, error) {
 	recursively := strings.HasSuffix(dir, "/...")
 	if recursively {
 		dir = dir[:len(dir)-4]
 	}
-	return dir, recursively, genGoDir(dir, conf, recursively)
+	return dir, recursively, genGoDir(dir, conf, genTestPkg, recursively)
 }
 
-func genGoDir(dir string, conf *Config, recursively bool) (err error) {
+func genGoDir(dir string, conf *Config, genTestPkg, recursively bool) (err error) {
 	if recursively {
 		return filepath.WalkDir(dir, func(path string, d fs.DirEntry, err error) error {
 			if err == nil && d.IsDir() {
 				if strings.HasPrefix(d.Name(), "_") { // skip _
 					return filepath.SkipDir
 				}
-				err = genGoIn(path, conf, true)
+				err = genGoIn(path, conf, genTestPkg, true)
 				if err != nil {
 					fmt.Fprintln(os.Stderr, err)
 				}
@@ -61,11 +61,11 @@ func genGoDir(dir string, conf *Config, recursively bool) (err error) {
 			return err
 		})
 	}
-	return genGoIn(dir, conf, false)
+	return genGoIn(dir, conf, genTestPkg, false)
 }
 
-func genGoIn(dir string, conf *Config, prompt bool) (err error) {
-	out, test, err := LoadDir(dir, conf)
+func genGoIn(dir string, conf *Config, genTestPkg, prompt bool) (err error) {
+	out, test, err := LoadDir(dir, conf, genTestPkg)
 	if err != nil {
 		if err == syscall.ENOENT { // no Go+ source files
 			return nil
@@ -99,6 +99,11 @@ func genGoIn(dir string, conf *Config, prompt bool) (err error) {
 
 // -----------------------------------------------------------------------------
 
+const (
+	modWritable = 0755
+	modReadonly = 0555
+)
+
 func GenGoPkgPath(workDir, pkgPath string, conf *Config, allowExtern bool) (localDir string, recursively bool, err error) {
 	recursively = strings.HasSuffix(pkgPath, "/...")
 	if recursively {
@@ -108,10 +113,10 @@ func GenGoPkgPath(workDir, pkgPath string, conf *Config, allowExtern bool) (loca
 	mod, err := gopmod.Load(workDir, 0)
 	if err == syscall.ENOENT && allowExtern {
 		remotePkgPathDo(pkgPath, func(dir string) {
-			os.Chmod(dir, 0755)
-			defer os.Chmod(dir, 0555)
+			os.Chmod(dir, modWritable)
+			defer os.Chmod(dir, modReadonly)
 			localDir = dir
-			err = genGoDir(dir, conf, recursively)
+			err = genGoDir(dir, conf, false, recursively)
 		}, func(e error) {
 			err = e
 		})
@@ -126,10 +131,10 @@ func GenGoPkgPath(workDir, pkgPath string, conf *Config, allowExtern bool) (loca
 	}
 	localDir = pkg.Dir
 	if pkg.Type == gopmod.PkgtExtern {
-		os.Chmod(localDir, 0755)
-		defer os.Chmod(localDir, 0555)
+		os.Chmod(localDir, modWritable)
+		defer os.Chmod(localDir, modReadonly)
 	}
-	err = genGoDir(localDir, conf, recursively)
+	err = genGoDir(localDir, conf, false, recursively)
 	return
 }
 
diff --git a/imp.go b/imp.go
index 3111a613..fef5b582 100644
--- a/imp.go
+++ b/imp.go
@@ -19,9 +19,12 @@ package gop
 import (
 	"go/token"
 	"go/types"
+	"os"
+	"path/filepath"
 	"strings"
 
 	"github.com/goplus/gox/packages"
+	"github.com/goplus/mod/env"
 	"github.com/goplus/mod/gopmod"
 	"github.com/goplus/mod/modfetch"
 )
@@ -31,16 +34,17 @@ import (
 type Importer struct {
 	impFrom *packages.Importer
 	mod     *gopmod.Module
+	gop     *env.Gop
 	gopRoot string
 }
 
-func NewImporter(mod *gopmod.Module, fset *token.FileSet, gopRoot string) *Importer {
+func NewImporter(mod *gopmod.Module, gop *env.Gop, fset *token.FileSet) *Importer {
 	dir := ""
 	if mod.IsValid() {
 		dir = mod.Root()
 	}
 	impFrom := packages.NewImporter(fset, dir)
-	return &Importer{mod: mod, gopRoot: gopRoot, impFrom: impFrom}
+	return &Importer{mod: mod, gopRoot: gop.Root, gop: gop, impFrom: impFrom}
 }
 
 func (p *Importer) Import(pkgPath string) (pkg *types.Package, err error) {
@@ -49,6 +53,11 @@ func (p *Importer) Import(pkgPath string) (pkg *types.Package, err error) {
 	)
 	if strings.HasPrefix(pkgPath, gop) {
 		if suffix := pkgPath[len(gop):]; suffix == "" || suffix[0] == '/' {
+			if suffix == "/cl/internal/gop-in-go/foo" {
+				if err = p.genGoExtern(p.gopRoot+suffix, false); err != nil {
+					return
+				}
+			}
 			return p.impFrom.ImportFrom(pkgPath, p.gopRoot, 0)
 		}
 	}
@@ -58,15 +67,34 @@ func (p *Importer) Import(pkgPath string) (pkg *types.Package, err error) {
 			if e != nil {
 				return nil, e
 			}
-			if modVer.Version != "" {
+			isExtern := modVer.Version != ""
+			if isExtern {
 				if _, err = modfetch.Get(modVer.String()); err != nil {
 					return
 				}
 			}
-			return p.impFrom.ImportFrom(pkgPath, ret.Dir, 0)
+			modfile := filepath.Join(ret.ModDir, "gop.mod")
+			if _, e := os.Lstat(modfile); e == nil { // has gop.mod
+				if err = p.genGoExtern(ret.Dir, isExtern); err != nil {
+					return
+				}
+			}
+			return p.impFrom.ImportFrom(pkgPath, ret.ModDir, 0)
 		}
 	}
 	return p.impFrom.Import(pkgPath)
 }
 
+func (p *Importer) genGoExtern(dir string, isExtern bool) (err error) {
+	genfile := filepath.Join(dir, autoGenFile)
+	if _, err = os.Lstat(genfile); err == nil { // has gop_autogen.go
+		return
+	}
+	if isExtern {
+		os.Chmod(dir, modWritable)
+		defer os.Chmod(dir, modReadonly)
+	}
+	return genGoIn(dir, &Config{Gop: p.gop, Importer: p}, false, false)
+}
+
 // -----------------------------------------------------------------------------
diff --git a/load.go b/load.go
index e37221ee..88ab6df9 100644
--- a/load.go
+++ b/load.go
@@ -19,6 +19,7 @@ package gop
 import (
 	"errors"
 	"go/token"
+	"go/types"
 	"io/fs"
 	"path/filepath"
 	"strings"
@@ -34,9 +35,10 @@ import (
 )
 
 type Config struct {
-	Gop    *env.Gop
-	Fset   *token.FileSet
-	Filter func(fs.FileInfo) bool
+	Gop      *env.Gop
+	Fset     *token.FileSet
+	Filter   func(fs.FileInfo) bool
+	Importer types.Importer
 
 	DontUpdateGoMod     bool
 	DontCheckModChanged bool
@@ -77,7 +79,7 @@ func lookupPub(mod *gopmod.Module) func(pkgPath string) (pubfile string, err err
 
 // -----------------------------------------------------------------------------
 
-func LoadDir(dir string, conf *Config) (out, test *gox.Package, err error) {
+func LoadDir(dir string, conf *Config, genTestPkg bool) (out, test *gox.Package, err error) {
 	if conf == nil {
 		conf = new(Config)
 	}
@@ -103,11 +105,16 @@ func LoadDir(dir string, conf *Config) (out, test *gox.Package, err error) {
 		return
 	}
 
+	imp := conf.Importer
+	if imp == nil {
+		imp = NewImporter(mod, gop, fset)
+	}
+
 	var pkgTest *ast.Package
 	var clConf = &cl.Config{
 		WorkingDir:  dir,
 		Fset:        fset,
-		Importer:    NewImporter(mod, fset, gop.Root),
+		Importer:    imp,
 		LookupClass: mod.LookupClass,
 		LookupPub:   lookupPub(mod),
 	}
@@ -133,7 +140,7 @@ func LoadDir(dir string, conf *Config) (out, test *gox.Package, err error) {
 	if out == nil {
 		return nil, nil, syscall.ENOENT
 	}
-	if pkgTest != nil {
+	if pkgTest != nil && genTestPkg {
 		test, err = cl.NewPackage("", pkgTest, clConf)
 	}
 	return
@@ -166,9 +173,13 @@ func LoadFiles(files []string, conf *Config) (out *gox.Package, err error) {
 		return nil, errMultiPackges
 	}
 	for _, pkg := range pkgs {
+		imp := conf.Importer
+		if imp == nil {
+			imp = NewImporter(mod, gop, fset)
+		}
 		out, err = cl.NewPackage("", pkg, &cl.Config{
 			Fset:        fset,
-			Importer:    NewImporter(mod, fset, gop.Root),
+			Importer:    imp,
 			LookupClass: mod.LookupClass,
 			LookupPub:   lookupPub(mod),
 		})


Test patch:
diff --git a/cl/compile_test.go b/cl/compile_test.go
index cb560c52..ebf1386f 100644
--- a/cl/compile_test.go
+++ b/cl/compile_test.go
@@ -23,13 +23,14 @@ import (
 	"sync"
 	"testing"
 
+	"github.com/goplus/gop"
 	"github.com/goplus/gop/cl"
 	"github.com/goplus/gop/parser"
 	"github.com/goplus/gop/parser/parsertest"
 	"github.com/goplus/gop/scanner"
 	"github.com/goplus/gop/token"
 	"github.com/goplus/gox"
-	"github.com/goplus/gox/packages"
+	"github.com/goplus/mod/env"
 )
 
 const (
@@ -45,7 +46,7 @@ func init() {
 	gox.SetDebug(gox.DbgFlagAll)
 	cl.SetDebug(cl.DbgFlagAll)
 	gblFset = token.NewFileSet()
-	imp := packages.NewImporter(gblFset)
+	imp := gop.NewImporter(nil, &env.Gop{Root: gopRootDir, Version: "1.0"}, gblFset)
 	gblConf = &cl.Config{
 		Fset:          gblFset,
 		Importer:      imp,
@@ -3421,7 +3422,7 @@ func removeAutogenFiles() {
 	os.Remove("./internal/gop-in-go/foo/gop_autogen2_test.go")
 }
 
-func _TestImportGopPkg(t *testing.T) {
+func TestImportGopPkg(t *testing.T) {
 	autogen.Lock()
 	defer autogen.Unlock()
 
@@ -3445,15 +3446,6 @@ func main() {
 }
 
 func TestCallDep(t *testing.T) {
-	const (
-		cachedir  = "../.gop"
-		cachefile = cachedir + "/gop.cache"
-	)
-	os.Remove(cachefile)
-	defer func() {
-		os.Remove(cachefile)
-		os.Remove(cachedir)
-	}()
 	for i := 0; i < 2; i++ {
 		gopClTest(t, `
 import (