Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: use modfile package to write modfile #1077

Merged
merged 11 commits into from
Oct 5, 2023
2 changes: 1 addition & 1 deletion gnovm/cmd/gno/mod.go
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ func execModDownload(cfg *modDownloadCfg, args []string, io *commands.IO) error
}

// write go.mod file
err = gomod.WriteToPath(filepath.Join(path, "go.mod"))
err = gomod.Write(filepath.Join(path, "go.mod"))
if err != nil {
return fmt.Errorf("write go.mod file: %w", err)
}
Expand Down
162 changes: 113 additions & 49 deletions gnovm/pkg/gnomod/file.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
// Some part of file is copied and modified from
// golang.org/x/mod/modfile/read.go
//
// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in here[1].
//
// [1]: https://cs.opensource.google/go/x/mod/+/master:LICENSE

package gnomod

import (
Expand All @@ -24,6 +33,100 @@ type File struct {
Syntax *modfile.FileSyntax
}

// AddRequire sets the first require line for path to version vers,
// preserving any existing comments for that line and removing all
// other lines for path.
//
// If no line currently exists for path, AddRequire adds a new line
// at the end of the last require block.
func (f *File) AddRequire(path, vers string) error {
need := true
for _, r := range f.Require {
if r.Mod.Path == path {
if need {
r.Mod.Version = vers
updateLine(r.Syntax, "require", modfile.AutoQuote(path), vers)
need = false
} else {
markLineAsRemoved(r.Syntax)
*r = modfile.Require{}
}
}
}

if need {
f.AddNewRequire(path, vers, false)
}
return nil
}

// AddNewRequire adds a new require line for path at version vers at the end of
// the last require block, regardless of any existing require lines for path.
func (f *File) AddNewRequire(path, vers string, indirect bool) {
line := addLine(f.Syntax, nil, "require", modfile.AutoQuote(path), vers)
r := &modfile.Require{
Mod: module.Version{Path: path, Version: vers},
Syntax: line,
}
setIndirect(r, indirect)
f.Require = append(f.Require, r)
}

func (f *File) AddModuleStmt(path string) error {
if f.Syntax == nil {
f.Syntax = new(modfile.FileSyntax)
}
if f.Module == nil {
f.Module = &modfile.Module{
Mod: module.Version{Path: path},
Syntax: addLine(f.Syntax, nil, "module", modfile.AutoQuote(path)),
}
} else {
f.Module.Mod.Path = path
updateLine(f.Module.Syntax, "module", modfile.AutoQuote(path))
}
return nil
}

func (f *File) AddComment(text string) {
if f.Syntax == nil {
f.Syntax = new(modfile.FileSyntax)
}
f.Syntax.Stmt = append(f.Syntax.Stmt, &modfile.CommentBlock{
Comments: modfile.Comments{
Before: []modfile.Comment{
{
Token: text,
},
},
},
})
}

func (f *File) AddReplace(oldPath, oldVers, newPath, newVers string) error {
return addReplace(f.Syntax, &f.Replace, oldPath, oldVers, newPath, newVers)
}

func (f *File) DropRequire(path string) error {
for _, r := range f.Require {
if r.Mod.Path == path {
markLineAsRemoved(r.Syntax)
*r = modfile.Require{}
}
}
return nil
}

func (f *File) DropReplace(oldPath, oldVers string) error {
for _, r := range f.Replace {
if r.Old.Path == oldPath && r.Old.Version == oldVers {
markLineAsRemoved(r.Syntax)
*r = modfile.Replace{}
}
}
return nil
}

// Validate validates gno.mod
func (f *File) Validate() error {
if f.Module == nil {
Expand Down Expand Up @@ -73,13 +176,8 @@ func (f *File) FetchDeps(path string, remote string, verbose bool) error {
return fmt.Errorf("writepackage: %w", err)
}

modFile := &File{
Module: &modfile.Module{
Mod: module.Version{
Path: mod.Path,
},
},
}
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) {
Expand All @@ -92,13 +190,7 @@ func (f *File) FetchDeps(path string, remote string, verbose bool) error {

if strings.HasPrefix(path, gnolang.ImportPrefix) {
path = strings.TrimPrefix(path, gnolang.ImportPrefix+"/examples/")
modFile.Require = append(modFile.Require, &modfile.Require{
Mod: module.Version{
Path: path,
Version: "v0.0.0", // TODO: Use latest?
},
Indirect: true,
})
modFile.AddNewRequire(path, "v0.0.0-latest", true)
}
}

Expand All @@ -112,7 +204,7 @@ func (f *File) FetchDeps(path string, remote string, verbose bool) error {
}
pkgPath := PackageDir(path, mod)
goModFilePath := filepath.Join(pkgPath, "go.mod")
err = goMod.WriteToPath(goModFilePath)
err = goMod.Write(goModFilePath)
if err != nil {
return err
}
Expand All @@ -121,42 +213,14 @@ func (f *File) FetchDeps(path string, remote string, verbose bool) error {
return nil
}

// WriteToPath writes file to the given absolute file path
// TODO: Find better way to do this. Try to use `modfile`
// package to manage this.
func (f *File) WriteToPath(absFilePath string) error {
if f.Module == nil {
return errors.New("writing go.mod: module not found")
}

data := "module " + f.Module.Mod.Path + "\n"

if f.Go != nil {
data += "\ngo " + f.Go.Version + "\n"
}

if f.Require != nil {
data += "\nrequire (" + "\n"
for _, req := range f.Require {
data += "\t" + req.Mod.Path + " " + req.Mod.Version + "\n"
}
data += ")\n"
}

if f.Replace != nil {
data += "\nreplace (" + "\n"
for _, rep := range f.Replace {
data += "\t" + rep.Old.Path + " " + rep.Old.Version +
" => " + rep.New.Path + "\n"
}
data += ")\n"
}

err := os.WriteFile(absFilePath, []byte(data), 0o644)
// writes file to the given absolute file path
func (f *File) Write(fname string) error {
harry-hov marked this conversation as resolved.
Show resolved Hide resolved
f.Syntax.Cleanup()
data := modfile.Format(f.Syntax)
err := os.WriteFile(fname, data, 0o644)
if err != nil {
return fmt.Errorf("writefile %q: %w", absFilePath, err)
return fmt.Errorf("writefile %q: %w", fname, err)
}

return nil
}

Expand Down
37 changes: 13 additions & 24 deletions gnovm/pkg/gnomod/gnomod.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ func GnoToGoMod(f File) (*File, error) {

if strings.HasPrefix(f.Module.Mod.Path, gnolang.GnoRealmPkgsPrefixBefore) ||
strings.HasPrefix(f.Module.Mod.Path, gnolang.GnoPackagePrefixBefore) {
f.Module.Mod.Path = gnolang.ImportPrefix + "/examples/" + f.Module.Mod.Path
f.AddModuleStmt(gnolang.ImportPrefix + "/examples/" + f.Module.Mod.Path)
}

for i := range f.Require {
Expand All @@ -113,20 +113,17 @@ func GnoToGoMod(f File) (*File, error) {
path := f.Require[i].Mod.Path
if strings.HasPrefix(f.Require[i].Mod.Path, gnolang.GnoRealmPkgsPrefixBefore) ||
strings.HasPrefix(f.Require[i].Mod.Path, gnolang.GnoPackagePrefixBefore) {
f.Require[i].Mod.Path = gnolang.ImportPrefix + "/examples/" + f.Require[i].Mod.Path
// Add dependency with a modified import path
f.AddRequire(gnolang.ImportPrefix+"/examples/"+f.Require[i].Mod.Path, f.Require[i].Mod.Version)
}

f.Replace = append(f.Replace, &modfile.Replace{
Old: module.Version{
Path: f.Require[i].Mod.Path,
Version: f.Require[i].Mod.Version,
},
New: module.Version{
Path: filepath.Join(gnoModPath, path),
},
})
f.AddReplace(f.Require[i].Mod.Path, f.Require[i].Mod.Version, filepath.Join(gnoModPath, path), "")
// Remove the old require since the new dependency was added above
f.DropRequire(f.Require[i].Mod.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.
//
Expand All @@ -153,14 +150,11 @@ func GnoToGoMod(f File) (*File, error) {
// ```
//
// Remove `gno.land/p/demo/avl v1.2.3 => gno.land/p/demo/avl v3.2.1`.
repl := make([]*modfile.Replace, 0, len(f.Replace))
for _, r := range f.Replace {
if !modfile.IsDirectoryPath(r.New.Path) {
continue
f.DropReplace(r.Old.Path, r.Old.Version)
}
repl = append(repl, r)
}
f.Replace = repl

return &f, nil
}
Expand Down Expand Up @@ -215,14 +209,9 @@ func CreateGnoModFile(rootDir, modPath string) error {
return err
}

modFile := &File{
Module: &modfile.Module{
Mod: module.Version{
Path: modPath,
},
},
}
modFile.WriteToPath(filepath.Join(rootDir, "gno.mod"))
modfile := new(File)
modfile.AddModuleStmt(modPath)
modfile.Write(filepath.Join(rootDir, "gno.mod"))

return nil
}
Expand Down
Loading