1
0
mirror of https://github.com/golang/go synced 2024-09-24 07:20:14 -06:00

cmd/go: improve the creation and editing of go.work files

This change changes go.work files so that directory paths are clearly
distinguished from module paths by either being rooted absolute paths or
starting with '.' or '..' path elements if they are relative paths.
go mod initwork now checks that the go.work file doesn't already exist
before creating it, and gomod initwork and gomod editwork look up the
module path corresponding to a directory and write it to the directory
directive's comment.

For #45713

Change-Id: I6983779059b7de6fc83d359280ceffb263f6b641
Reviewed-on: https://go-review.googlesource.com/c/go/+/347591
Trust: Michael Matloob <matloob@golang.org>
Run-TryBot: Michael Matloob <matloob@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Jay Conrod <jayconrod@google.com>
This commit is contained in:
Michael Matloob 2021-09-03 13:33:37 -04:00
parent c8d4fe2adc
commit 37c9552e06
5 changed files with 83 additions and 40 deletions

View File

@ -15,6 +15,7 @@ import (
"encoding/json"
"errors"
"os"
"path/filepath"
"strings"
"golang.org/x/mod/modfile"
@ -191,7 +192,13 @@ func runEditwork(ctx context.Context, cmd *base.Command, args []string) {
// flagEditworkDirectory implements the -directory flag.
func flagEditworkDirectory(arg string) {
workedits = append(workedits, func(f *modfile.WorkFile) {
if err := f.AddDirectory(arg, ""); err != nil {
_, mf, err := modload.ReadModFile(filepath.Join(arg, "go.mod"), nil)
modulePath := ""
if err == nil {
modulePath = mf.Module.Mod.Path
}
f.AddDirectory(modload.ToDirectoryPath(arg), modulePath)
if err := f.AddDirectory(modload.ToDirectoryPath(arg), ""); err != nil {
base.Fatalf("go mod: -directory=%s: %v", arg, err)
}
})
@ -200,7 +207,7 @@ func flagEditworkDirectory(arg string) {
// flagEditworkDropDirectory implements the -dropdirectory flag.
func flagEditworkDropDirectory(arg string) {
workedits = append(workedits, func(f *modfile.WorkFile) {
if err := f.DropDirectory(arg); err != nil {
if err := f.DropDirectory(modload.ToDirectoryPath(arg)); err != nil {
base.Fatalf("go mod: -dropdirectory=%s: %v", arg, err)
}
})

View File

@ -634,31 +634,12 @@ func loadModFile(ctx context.Context) (rs *Requirements, needCommit bool) {
var indices []*modFileIndex
for _, modroot := range modRoots {
gomod := modFilePath(modroot)
var data []byte
var err error
if gomodActual, ok := fsys.OverlayPath(gomod); ok {
// Don't lock go.mod if it's part of the overlay.
// On Plan 9, locking requires chmod, and we don't want to modify any file
// in the overlay. See #44700.
data, err = os.ReadFile(gomodActual)
} else {
data, err = lockedfile.Read(gomodActual)
}
var fixed bool
data, f, err := ReadModFile(gomod, fixVersion(ctx, &fixed))
if err != nil {
base.Fatalf("go: %v", err)
}
var fixed bool
f, err := modfile.Parse(gomod, data, fixVersion(ctx, &fixed))
if err != nil {
// Errors returned by modfile.Parse begin with file:line.
base.Fatalf("go: errors parsing go.mod:\n%s\n", err)
}
if f.Module == nil {
// No module declaration. Must add module path.
base.Fatalf("go: no module declaration in go.mod. To specify the module path:\n\tgo mod edit -module=example.com/mod")
}
modFiles = append(modFiles, f)
mainModule := f.Module.Mod
mainModules = append(mainModules, mainModule)
@ -819,7 +800,9 @@ func CreateModFile(ctx context.Context, modPath string) {
// CreateWorkFile initializes a new workspace by creating a go.work file.
func CreateWorkFile(ctx context.Context, workFile string, modDirs []string) {
_ = TODOWorkspaces("Report an error if the file already exists.")
if _, err := fsys.Stat(workFile); err == nil {
base.Fatalf("go: %s already exists", workFile)
}
goV := LatestGoVersion() // Use current Go version by default
workF := new(modfile.WorkFile)
@ -827,12 +810,18 @@ func CreateWorkFile(ctx context.Context, workFile string, modDirs []string) {
workF.AddGoStmt(goV)
for _, dir := range modDirs {
_ = TODOWorkspaces("Add the module path of the module.")
workF.AddDirectory(dir, "")
_, f, err := ReadModFile(filepath.Join(dir, "go.mod"), nil)
if err != nil {
if os.IsNotExist(err) {
base.Fatalf("go: creating workspace file: no go.mod file exists in directory %v", dir)
}
base.Fatalf("go: error parsing go.mod in directory %s: %v", dir, err)
}
workF.AddDirectory(ToDirectoryPath(dir), f.Module.Mod.Path)
}
data := modfile.Format(workF.Syntax)
lockedfile.Write(workFile, bytes.NewReader(data), 0644)
lockedfile.Write(workFile, bytes.NewReader(data), 0666)
}
// fixVersion returns a modfile.VersionFixer implemented using the Query function.

View File

@ -47,6 +47,34 @@ const (
separateIndirectVersionV = "v1.17"
)
// ReadModFile reads and parses the mod file at gomod. ReadModFile properly applies the
// overlay, locks the file while reading, and applies fix, if applicable.
func ReadModFile(gomod string, fix modfile.VersionFixer) (data []byte, f *modfile.File, err error) {
if gomodActual, ok := fsys.OverlayPath(gomod); ok {
// Don't lock go.mod if it's part of the overlay.
// On Plan 9, locking requires chmod, and we don't want to modify any file
// in the overlay. See #44700.
data, err = os.ReadFile(gomodActual)
} else {
data, err = lockedfile.Read(gomodActual)
}
if err != nil {
return nil, nil, err
}
f, err = modfile.Parse(gomod, data, fix)
if err != nil {
// Errors returned by modfile.Parse begin with file:line.
return nil, nil, fmt.Errorf("errors parsing go.mod:\n%s\n", err)
}
if f.Module == nil {
// No module declaration. Must add module path.
return nil, nil, errors.New("no module declaration in go.mod. To specify the module path:\n\tgo mod edit -module=example.com/mod")
}
return data, f, err
}
// modFileGoVersion returns the (non-empty) Go version at which the requirements
// in modFile are interpreted, or the latest Go version if modFile is nil.
func modFileGoVersion(modFile *modfile.File) string {
@ -739,3 +767,15 @@ func queryLatestVersionIgnoringRetractions(ctx context.Context, path string) (la
}
var latestVersionIgnoringRetractionsCache par.Cache // path → queryLatestVersionIgnoringRetractions result
// ToDirectoryPath adds a prefix if necessary so that path in unambiguously
// an absolute path or a relative path starting with a '.' or '..'
// path component.
func ToDirectoryPath(path string) string {
if modfile.IsDirectoryPath(path) {
return path
}
// The path is not a relative path or an absolute path, so make it relative
// to the current directory.
return "./" + filepath.ToSlash(filepath.Clean(path))
}

View File

@ -1,3 +1,6 @@
! go mod initwork doesnotexist
stderr 'go: creating workspace file: no go.mod file exists in directory doesnotexist'
go mod initwork ./a ./b
cmp go.work go.work.want

View File

@ -33,32 +33,36 @@ cmp stdout go.work.want_json
go mod editwork -print -fmt -workfile unformatted
cmp stdout formatted
-- m/go.mod --
module m
go 1.18
-- go.work.want_initial --
go 1.18
directory m
directory ./m
-- go.work.want_directory_n --
go 1.18
directory (
m
n
./m
./n
)
-- go.work.want_go_118 --
go 1.18
directory (
m
n
./m
./n
)
-- go.work.want_dropdirectory_m --
go 1.18
directory n
directory ./n
-- go.work.want_add_replaces --
go 1.18
directory n
directory ./n
replace (
x.1 v1.3.0 => y.1 v1.4.0
@ -69,9 +73,9 @@ go 1.18
directory (
../a
./c
./n
/b
c
n
)
replace (
@ -83,7 +87,7 @@ go 1.18
directory (
../a
c
./c
)
replace (
@ -95,7 +99,7 @@ go 1.18
directory (
../a
c
./c
)
replace x.1 v1.3.0 => y.1 v1.4.0
@ -104,7 +108,7 @@ go 1.19
directory (
../a
b
./b
)
replace x.1 v1.4.0 => ../z
@ -116,7 +120,7 @@ replace x.1 v1.4.0 => ../z
"DiskPath": "../a"
},
{
"DiskPath": "b"
"DiskPath": "./b"
}
],
"Replace": [