diff --git a/gnovm/cmd/gno/mod.go b/gnovm/cmd/gno/mod.go index fec1b0ab2c1..b5d0fcdf333 100644 --- a/gnovm/cmd/gno/mod.go +++ b/gnovm/cmd/gno/mod.go @@ -13,7 +13,6 @@ import ( "github.com/gnolang/gno/gnovm/pkg/gnomod" "github.com/gnolang/gno/tm2/pkg/commands" - "github.com/gnolang/gno/tm2/pkg/errors" "go.uber.org/multierr" ) @@ -29,7 +28,6 @@ func newModCmd(io commands.IO) *commands.Command { ) cmd.AddSubCommands( - newModDownloadCmd(io), newModInitCmd(), newModTidy(io), newModWhy(io), @@ -38,22 +36,6 @@ func newModCmd(io commands.IO) *commands.Command { return cmd } -func newModDownloadCmd(io commands.IO) *commands.Command { - cfg := &modDownloadCfg{} - - return commands.NewCommand( - commands.Metadata{ - Name: "download", - ShortUsage: "download [flags]", - ShortHelp: "download modules to local cache", - }, - cfg, - func(_ context.Context, args []string) error { - return execModDownload(cfg, args, io) - }, - ) -} - func newModInitCmd() *commands.Command { return commands.NewCommand( commands.Metadata{ @@ -122,79 +104,6 @@ For example: ) } -type modDownloadCfg struct { - remote string - verbose bool -} - -func (c *modDownloadCfg) RegisterFlags(fs *flag.FlagSet) { - fs.StringVar( - &c.remote, - "remote", - "test3.gno.land:26657", - "remote for fetching gno modules", - ) - - fs.BoolVar( - &c.verbose, - "v", - false, - "verbose output when running", - ) -} - -func execModDownload(cfg *modDownloadCfg, args []string, io commands.IO) error { - if len(args) > 0 { - return flag.ErrHelp - } - - path, err := os.Getwd() - if err != nil { - return err - } - modPath := filepath.Join(path, "gno.mod") - if !isFileExist(modPath) { - return errors.New("gno.mod not found") - } - - // read gno.mod - data, err := os.ReadFile(modPath) - if err != nil { - return fmt.Errorf("readfile %q: %w", modPath, err) - } - - // parse gno.mod - gnoMod, err := gnomod.Parse(modPath, data) - if err != nil { - return fmt.Errorf("parse: %w", err) - } - // sanitize gno.mod - gnoMod.Sanitize() - - // validate gno.mod - if err := gnoMod.Validate(); err != nil { - return fmt.Errorf("validate: %w", err) - } - - // fetch dependencies - if err := gnoMod.FetchDeps(gnomod.GetGnoModPath(), cfg.remote, cfg.verbose); err != nil { - return fmt.Errorf("fetch: %w", err) - } - - gomod, err := gnomod.GnoToGoMod(*gnoMod) - if err != nil { - return fmt.Errorf("sanitize: %w", err) - } - - // write go.mod file - err = gomod.Write(filepath.Join(path, "go.mod")) - if err != nil { - return fmt.Errorf("write go.mod file: %w", err) - } - - return nil -} - func execModInit(args []string) error { if len(args) > 1 { return flag.ErrHelp diff --git a/gnovm/cmd/gno/mod_test.go b/gnovm/cmd/gno/mod_test.go index d35ab311b6c..705d61c8e46 100644 --- a/gnovm/cmd/gno/mod_test.go +++ b/gnovm/cmd/gno/mod_test.go @@ -16,70 +16,6 @@ func TestModApp(t *testing.T) { errShouldBe: "flag: help requested", }, - // test `gno mod download` - { - args: []string{"mod", "download"}, - testDir: "../../tests/integ/empty_dir", - simulateExternalRepo: true, - errShouldBe: "gno.mod not found", - }, - { - args: []string{"mod", "download"}, - testDir: "../../tests/integ/empty_gnomod", - simulateExternalRepo: true, - errShouldBe: "validate: requires module", - }, - { - args: []string{"mod", "download"}, - testDir: "../../tests/integ/invalid_module_name", - simulateExternalRepo: true, - errShouldContain: "usage: module module/path", - }, - { - args: []string{"mod", "download"}, - testDir: "../../tests/integ/minimalist_gnomod", - simulateExternalRepo: true, - }, - { - args: []string{"mod", "download"}, - testDir: "../../tests/integ/require_remote_module", - simulateExternalRepo: true, - }, - { - args: []string{"mod", "download"}, - testDir: "../../tests/integ/require_invalid_module", - simulateExternalRepo: true, - errShouldContain: "fetch: writepackage: querychain", - }, - { - args: []string{"mod", "download"}, - testDir: "../../tests/integ/invalid_module_version1", - simulateExternalRepo: true, - errShouldContain: "usage: require module/path v1.2.3", - }, - { - args: []string{"mod", "download"}, - testDir: "../../tests/integ/invalid_module_version2", - simulateExternalRepo: true, - errShouldContain: "invalid: must be of the form v1.2.3", - }, - { - args: []string{"mod", "download"}, - testDir: "../../tests/integ/replace_with_dir", - simulateExternalRepo: true, - }, - { - args: []string{"mod", "download"}, - testDir: "../../tests/integ/replace_with_module", - simulateExternalRepo: true, - }, - { - args: []string{"mod", "download"}, - testDir: "../../tests/integ/replace_with_invalid_module", - simulateExternalRepo: true, - errShouldContain: "fetch: writepackage: querychain", - }, - // test `gno mod init` with no module name { args: []string{"mod", "init"}, diff --git a/gnovm/cmd/gno/util.go b/gnovm/cmd/gno/util.go index 90aedd5d27a..b6032478ca5 100644 --- a/gnovm/cmd/gno/util.go +++ b/gnovm/cmd/gno/util.go @@ -16,11 +16,6 @@ func isGnoFile(f fs.DirEntry) bool { return !strings.HasPrefix(name, ".") && strings.HasSuffix(name, ".gno") && !f.IsDir() } -func isFileExist(path string) bool { - _, err := os.Stat(path) - return err == nil -} - func gnoFilesFromArgsRecursively(args []string) ([]string, error) { var paths []string diff --git a/gnovm/pkg/gnomod/fetch.go b/gnovm/pkg/gnomod/fetch.go deleted file mode 100644 index 24aaac2f9d4..00000000000 --- a/gnovm/pkg/gnomod/fetch.go +++ /dev/null @@ -1,30 +0,0 @@ -package gnomod - -import ( - "fmt" - - abci "github.com/gnolang/gno/tm2/pkg/bft/abci/types" - "github.com/gnolang/gno/tm2/pkg/bft/rpc/client" -) - -func queryChain(remote string, qpath string, data []byte) (res *abci.ResponseQuery, err error) { - opts2 := client.ABCIQueryOptions{ - // Height: height, XXX - // Prove: false, XXX - } - cli, err := client.NewHTTPClient(remote) - if err != nil { - return nil, err - } - - qres, err := cli.ABCIQueryWithOptions(qpath, data, opts2) - if err != nil { - return nil, err - } - if qres.Response.Error != nil { - fmt.Printf("Log: %s\n", qres.Response.Log) - return nil, qres.Response.Error - } - - return &qres.Response, nil -} diff --git a/gnovm/pkg/gnomod/file.go b/gnovm/pkg/gnomod/file.go index b6ee95acac8..1b05ca61ea4 100644 --- a/gnovm/pkg/gnomod/file.go +++ b/gnovm/pkg/gnomod/file.go @@ -12,12 +12,8 @@ package gnomod import ( "errors" "fmt" - "log" "os" - "path/filepath" - "strings" - gno "github.com/gnolang/gno/gnovm/pkg/gnolang" "golang.org/x/mod/modfile" "golang.org/x/mod/module" ) @@ -146,68 +142,6 @@ func (f *File) Resolve(r *modfile.Require) module.Version { return r.Mod } -// FetchDeps fetches and writes gno.mod packages -// in GOPATH/pkg/gnomod/ -func (f *File) FetchDeps(path string, remote string, verbose bool) error { - for _, r := range f.Require { - mod := f.Resolve(r) - if r.Mod.Path != mod.Path { - if modfile.IsDirectoryPath(mod.Path) { - continue - } - } - indirect := "" - if r.Indirect { - indirect = "// indirect" - } - - _, err := os.Stat(PackageDir(path, mod)) - if !os.IsNotExist(err) { - if verbose { - log.Println("cached", mod.Path, indirect) - } - continue - } - if verbose { - log.Println("fetching", mod.Path, indirect) - } - requirements, err := writePackage(remote, path, mod.Path) - if err != nil { - return fmt.Errorf("writepackage: %w", err) - } - - modFile := new(File) - modFile.AddModuleStmt(mod.Path) - for _, req := range requirements { - path := req[1 : len(req)-1] // trim leading and trailing `"` - if strings.HasSuffix(path, modFile.Module.Mod.Path) { - continue - } - - if !gno.IsStdlib(path) { - modFile.AddNewRequire(path, "v0.0.0-latest", true) - } - } - - err = modFile.FetchDeps(path, remote, verbose) - if err != nil { - return err - } - goMod, err := GnoToGoMod(*modFile) - if err != nil { - return err - } - pkgPath := PackageDir(path, mod) - goModFilePath := filepath.Join(pkgPath, "go.mod") - err = goMod.Write(goModFilePath) - if err != nil { - return err - } - } - - return nil -} - // writes file to the given absolute file path func (f *File) Write(fname string) error { f.Syntax.Cleanup() diff --git a/gnovm/pkg/gnomod/file_test.go b/gnovm/pkg/gnomod/file_test.go deleted file mode 100644 index 7abfe16f340..00000000000 --- a/gnovm/pkg/gnomod/file_test.go +++ /dev/null @@ -1,142 +0,0 @@ -package gnomod - -import ( - "bytes" - "log" - "os" - "path/filepath" - "testing" - - "github.com/gnolang/gno/tm2/pkg/testutils" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "golang.org/x/mod/modfile" - "golang.org/x/mod/module" -) - -const testRemote string = "test3.gno.land:26657" - -func TestFetchDeps(t *testing.T) { - for _, tc := range []struct { - desc string - modFile File - errorShouldContain string - requirements []string - stdOutContains []string - cachedStdOutContains []string - }{ - { - desc: "not_exists", - modFile: File{ - Module: &modfile.Module{ - Mod: module.Version{ - Path: "testFetchDeps", - }, - }, - Require: []*modfile.Require{ - { - Mod: module.Version{ - Path: "gno.land/p/demo/does_not_exists", - Version: "v0.0.0", - }, - }, - }, - }, - errorShouldContain: "querychain (gno.land/p/demo/does_not_exists)", - }, { - desc: "fetch_gno.land/p/demo/avl", - modFile: File{ - Module: &modfile.Module{ - Mod: module.Version{ - Path: "testFetchDeps", - }, - }, - Require: []*modfile.Require{ - { - Mod: module.Version{ - Path: "gno.land/p/demo/avl", - Version: "v0.0.0", - }, - }, - }, - }, - requirements: []string{"avl"}, - stdOutContains: []string{ - "fetching gno.land/p/demo/avl", - }, - cachedStdOutContains: []string{ - "cached gno.land/p/demo/avl", - }, - }, { - desc: "fetch_gno.land/p/demo/blog", - modFile: File{ - Module: &modfile.Module{ - Mod: module.Version{ - Path: "testFetchDeps", - }, - }, - Require: []*modfile.Require{ - { - Mod: module.Version{ - Path: "gno.land/p/demo/blog", - Version: "v0.0.0", - }, - }, - }, - }, - requirements: []string{"avl", "blog", "ufmt"}, - stdOutContains: []string{ - "fetching gno.land/p/demo/blog", - "fetching gno.land/p/demo/avl // indirect", - "fetching gno.land/p/demo/ufmt // indirect", - }, - cachedStdOutContains: []string{ - "cached gno.land/p/demo/blog", - }, - }, - } { - t.Run(tc.desc, func(t *testing.T) { - var buf bytes.Buffer - log.SetOutput(&buf) - defer func() { - log.SetOutput(os.Stderr) - }() - - // Create test dir - dirPath, cleanUpFn := testutils.NewTestCaseDir(t) - assert.NotNil(t, dirPath) - defer cleanUpFn() - - // Fetching dependencies - err := tc.modFile.FetchDeps(dirPath, testRemote, true) - if tc.errorShouldContain != "" { - require.ErrorContains(t, err, tc.errorShouldContain) - } else { - require.Nil(t, err) - - // Read dir - entries, err := os.ReadDir(filepath.Join(dirPath, "gno.land", "p", "demo")) - require.Nil(t, err) - - // Check dir entries - assert.Equal(t, len(tc.requirements), len(entries)) - for _, e := range entries { - assert.Contains(t, tc.requirements, e.Name()) - } - - // Check logs - for _, c := range tc.stdOutContains { - assert.Contains(t, buf.String(), c) - } - - buf.Reset() - - // Try fetching again. Should be cached - tc.modFile.FetchDeps(dirPath, testRemote, true) - for _, c := range tc.cachedStdOutContains { - assert.Contains(t, buf.String(), c) - } - } - }) - } -} diff --git a/gnovm/pkg/gnomod/gnomod.go b/gnovm/pkg/gnomod/gnomod.go index 0effa532107..bf5df59a389 100644 --- a/gnovm/pkg/gnomod/gnomod.go +++ b/gnovm/pkg/gnomod/gnomod.go @@ -3,22 +3,16 @@ package gnomod import ( "errors" "fmt" - "go/parser" - gotoken "go/token" "os" "path/filepath" "strings" "github.com/gnolang/gno/gnovm/pkg/gnoenv" "github.com/gnolang/gno/gnovm/pkg/gnolang" - "github.com/gnolang/gno/gnovm/pkg/transpiler" - "github.com/gnolang/gno/tm2/pkg/std" "golang.org/x/mod/modfile" "golang.org/x/mod/module" ) -const queryPathFile = "vm/qfile" - // GetGnoModPath returns the path for gno modules func GetGnoModPath() string { return filepath.Join(gnoenv.HomeDir(), "pkg", "mod") @@ -37,119 +31,6 @@ func PackageDir(root string, v module.Version) string { return filepath.Join(root, v.Path) } -func writePackage(remote, basePath, pkgPath string) (requirements []string, err error) { - res, err := queryChain(remote, queryPathFile, []byte(pkgPath)) - if err != nil { - return nil, fmt.Errorf("querychain (%s): %w", pkgPath, err) - } - - dirPath, fileName := std.SplitFilepath(pkgPath) - if fileName == "" { - // Is Dir - // Create Dir if not exists - dirPath := filepath.Join(basePath, dirPath) - if _, err = os.Stat(dirPath); os.IsNotExist(err) { - if err = os.MkdirAll(dirPath, 0o755); err != nil { - return nil, fmt.Errorf("mkdir %q: %w", dirPath, err) - } - } - - files := strings.Split(string(res.Data), "\n") - for _, file := range files { - reqs, err := writePackage(remote, basePath, filepath.Join(pkgPath, file)) - if err != nil { - return nil, fmt.Errorf("writepackage: %w", err) - } - requirements = append(requirements, reqs...) - } - } else { - // Is File - // Transpile and write generated go file - file, err := parser.ParseFile(gotoken.NewFileSet(), fileName, res.Data, parser.ImportsOnly) - if err != nil { - return nil, fmt.Errorf("parse gno file: %w", err) - } - for _, i := range file.Imports { - requirements = append(requirements, i.Path.Value) - } - - // Write file - fileNameWithPath := filepath.Join(basePath, dirPath, fileName) - err = os.WriteFile(fileNameWithPath, res.Data, 0o644) - if err != nil { - return nil, fmt.Errorf("writefile %q: %w", fileNameWithPath, err) - } - } - - return removeDuplicateStr(requirements), nil -} - -// GnoToGoMod make necessary modifications in the gno.mod -// and return go.mod file. -func GnoToGoMod(f File) (*File, error) { - // TODO(morgan): good candidate to move to pkg/transpiler. - - gnoModPath := GetGnoModPath() - - if !gnolang.IsStdlib(f.Module.Mod.Path) { - f.AddModuleStmt(transpiler.TranspileImportPath(f.Module.Mod.Path)) - } - - for i := range f.Require { - mod, replaced := isReplaced(f.Require[i].Mod, f.Replace) - if replaced { - if modfile.IsDirectoryPath(mod.Path) { - continue - } - } - path := f.Require[i].Mod.Path - if !gnolang.IsStdlib(path) { - // Add dependency with a modified import path - f.AddRequire(transpiler.TranspileImportPath(path), f.Require[i].Mod.Version) - } - f.AddReplace(path, f.Require[i].Mod.Version, filepath.Join(gnoModPath, path), "") - // Remove the old require since the new dependency was added above - f.DropRequire(path) - } - - // Remove replacements that are not replaced by directories. - // - // Explanation: - // By this stage every replacement should be replace by dir. - // If not replaced by dir, remove it. - // - // e.g: - // - // ``` - // require ( - // gno.land/p/demo/avl v1.2.3 - // ) - // - // replace ( - // gno.land/p/demo/avl v1.2.3 => gno.land/p/demo/avl v3.2.1 - // ) - // ``` - // - // In above case we will fetch `gno.land/p/demo/avl v3.2.1` and - // replace will look something like: - // - // ``` - // replace ( - // gno.land/p/demo/avl v1.2.3 => gno.land/p/demo/avl v3.2.1 - // gno.land/p/demo/avl v3.2.1 => /path/to/avl/version/v3.2.1 - // ) - // ``` - // - // Remove `gno.land/p/demo/avl v1.2.3 => gno.land/p/demo/avl v3.2.1`. - for _, r := range f.Replace { - if !modfile.IsDirectoryPath(r.New.Path) { - f.DropReplace(r.Old.Path, r.Old.Version) - } - } - - return &f, nil -} - func CreateGnoModFile(rootDir, modPath string) error { if !filepath.IsAbs(rootDir) { return fmt.Errorf("dir %q is not absolute", rootDir) @@ -217,14 +98,3 @@ func isReplaced(mod module.Version, repl []*modfile.Replace) (module.Version, bo } return module.Version{}, false } - -func removeDuplicateStr(str []string) (res []string) { - m := make(map[string]struct{}, len(str)) - for _, s := range str { - if _, ok := m[s]; !ok { - m[s] = struct{}{} - res = append(res, s) - } - } - return -}