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 @@
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,
},
},
},
})

Check warning on line 103 in gnovm/pkg/gnomod/file.go

View check run for this annotation

Codecov / codecov/patch

gnovm/pkg/gnomod/file.go#L91-L103

Added lines #L91 - L103 were not covered by tests
}

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 @@
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 @@

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 @@
}
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 @@
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)

Check warning on line 222 in gnovm/pkg/gnomod/file.go

View check run for this annotation

Codecov / codecov/patch

gnovm/pkg/gnomod/file.go#L222

Added line #L222 was not covered by tests
}

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 @@

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 @@
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 @@
// ```
//
// 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)

Check warning on line 155 in gnovm/pkg/gnomod/gnomod.go

View check run for this annotation

Codecov / codecov/patch

gnovm/pkg/gnomod/gnomod.go#L155

Added line #L155 was not covered by tests
}
repl = append(repl, r)
}
f.Replace = repl

return &f, nil
}
Expand Down Expand Up @@ -215,14 +209,9 @@
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
Loading