"encoding/json"
"errors"
"os"
+ "path/filepath"
"strings"
"golang.org/x/mod/modfile"
// 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)
}
})
// 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)
}
})
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)
- }
- if err != nil {
- base.Fatalf("go: %v", err)
- }
-
var fixed bool
- f, err := modfile.Parse(gomod, data, fixVersion(ctx, &fixed))
+ data, f, err := ReadModFile(gomod, 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")
+ base.Fatalf("go: %v", err)
}
modFiles = append(modFiles, f)
// 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)
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.
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 {
}
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))
+}
+! 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
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
directory (
../a
+ ./c
+ ./n
/b
- c
- n
)
replace (
directory (
../a
- c
+ ./c
)
replace (
directory (
../a
- c
+ ./c
)
replace x.1 v1.3.0 => y.1 v1.4.0
directory (
../a
- b
+ ./b
)
replace x.1 v1.4.0 => ../z
"DiskPath": "../a"
},
{
- "DiskPath": "b"
+ "DiskPath": "./b"
}
],
"Replace": [