diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-16 19:23:18 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-16 19:23:18 +0000 |
commit | 43a123c1ae6613b3efeed291fa552ecd909d3acf (patch) | |
tree | fd92518b7024bc74031f78a1cf9e454b65e73665 /src/cmd/go/internal/workcmd/use.go | |
parent | Initial commit. (diff) | |
download | golang-1.20-43a123c1ae6613b3efeed291fa552ecd909d3acf.tar.xz golang-1.20-43a123c1ae6613b3efeed291fa552ecd909d3acf.zip |
Adding upstream version 1.20.14.upstream/1.20.14upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/cmd/go/internal/workcmd/use.go')
-rw-r--r-- | src/cmd/go/internal/workcmd/use.go | 213 |
1 files changed, 213 insertions, 0 deletions
diff --git a/src/cmd/go/internal/workcmd/use.go b/src/cmd/go/internal/workcmd/use.go new file mode 100644 index 0000000..be90989 --- /dev/null +++ b/src/cmd/go/internal/workcmd/use.go @@ -0,0 +1,213 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// go work use + +package workcmd + +import ( + "cmd/go/internal/base" + "cmd/go/internal/fsys" + "cmd/go/internal/modload" + "cmd/go/internal/str" + "context" + "fmt" + "io/fs" + "os" + "path/filepath" +) + +var cmdUse = &base.Command{ + UsageLine: "go work use [-r] moddirs", + Short: "add modules to workspace file", + Long: `Use provides a command-line interface for adding +directories, optionally recursively, to a go.work file. + +A use directive will be added to the go.work file for each argument +directory listed on the command line go.work file, if it exists on disk, +or removed from the go.work file if it does not exist on disk. + +The -r flag searches recursively for modules in the argument +directories, and the use command operates as if each of the directories +were specified as arguments: namely, use directives will be added for +directories that exist, and removed for directories that do not exist. + +See the workspaces reference at https://go.dev/ref/mod#workspaces +for more information. +`, +} + +var useR = cmdUse.Flag.Bool("r", false, "") + +func init() { + cmdUse.Run = runUse // break init cycle + + base.AddChdirFlag(&cmdUse.Flag) + base.AddModCommonFlags(&cmdUse.Flag) +} + +func runUse(ctx context.Context, cmd *base.Command, args []string) { + modload.ForceUseModules = true + + var gowork string + modload.InitWorkfile() + gowork = modload.WorkFilePath() + + if gowork == "" { + base.Fatalf("go: no go.work file found\n\t(run 'go work init' first or specify path using GOWORK environment variable)") + } + workFile, err := modload.ReadWorkFile(gowork) + if err != nil { + base.Fatalf("go: %v", err) + } + workDir := filepath.Dir(gowork) // Absolute, since gowork itself is absolute. + + haveDirs := make(map[string][]string) // absolute → original(s) + for _, use := range workFile.Use { + var abs string + if filepath.IsAbs(use.Path) { + abs = filepath.Clean(use.Path) + } else { + abs = filepath.Join(workDir, use.Path) + } + haveDirs[abs] = append(haveDirs[abs], use.Path) + } + + // keepDirs maps each absolute path to keep to the literal string to use for + // that path (either an absolute or a relative path), or the empty string if + // all entries for the absolute path should be removed. + keepDirs := make(map[string]string) + + // lookDir updates the entry in keepDirs for the directory dir, + // which is either absolute or relative to the current working directory + // (not necessarily the directory containing the workfile). + lookDir := func(dir string) { + absDir, dir := pathRel(workDir, dir) + + fi, err := fsys.Stat(filepath.Join(absDir, "go.mod")) + if err != nil { + if os.IsNotExist(err) { + keepDirs[absDir] = "" + } else { + base.Errorf("go: %v", err) + } + return + } + + if !fi.Mode().IsRegular() { + base.Errorf("go: %v is not regular", filepath.Join(dir, "go.mod")) + } + + if dup := keepDirs[absDir]; dup != "" && dup != dir { + base.Errorf(`go: already added "%s" as "%s"`, dir, dup) + } + keepDirs[absDir] = dir + } + + if len(args) == 0 { + base.Fatalf("go: 'go work use' requires one or more directory arguments") + } + for _, useDir := range args { + absArg, _ := pathRel(workDir, useDir) + + info, err := fsys.Stat(absArg) + if err != nil { + // Errors raised from os.Stat are formatted to be more user-friendly. + if os.IsNotExist(err) { + base.Errorf("go: directory %v does not exist", absArg) + } else { + base.Errorf("go: %v", err) + } + continue + } else if !info.IsDir() { + base.Errorf("go: %s is not a directory", absArg) + continue + } + + if !*useR { + lookDir(useDir) + continue + } + + // Add or remove entries for any subdirectories that still exist. + fsys.Walk(useDir, func(path string, info fs.FileInfo, err error) error { + if err != nil { + return err + } + + if !info.IsDir() { + if info.Mode()&fs.ModeSymlink != 0 { + if target, err := fsys.Stat(path); err == nil && target.IsDir() { + fmt.Fprintf(os.Stderr, "warning: ignoring symlink %s\n", path) + } + } + return nil + } + lookDir(path) + return nil + }) + + // Remove entries for subdirectories that no longer exist. + // Because they don't exist, they will be skipped by Walk. + for absDir := range haveDirs { + if str.HasFilePathPrefix(absDir, absArg) { + if _, ok := keepDirs[absDir]; !ok { + keepDirs[absDir] = "" // Mark for deletion. + } + } + } + } + + base.ExitIfErrors() + + for absDir, keepDir := range keepDirs { + nKept := 0 + for _, dir := range haveDirs[absDir] { + if dir == keepDir { // (note that dir is always non-empty) + nKept++ + } else { + workFile.DropUse(dir) + } + } + if keepDir != "" && nKept != 1 { + // If we kept more than one copy, delete them all. + // We'll recreate a unique copy with AddUse. + if nKept > 1 { + workFile.DropUse(keepDir) + } + workFile.AddUse(keepDir, "") + } + } + modload.UpdateWorkFile(workFile) + modload.WriteWorkFile(gowork, workFile) +} + +// pathRel returns the absolute and canonical forms of dir for use in a +// go.work file located in directory workDir. +// +// If dir is relative, it is intepreted relative to base.Cwd() +// and its canonical form is relative to workDir if possible. +// If dir is absolute or cannot be made relative to workDir, +// its canonical form is absolute. +// +// Canonical absolute paths are clean. +// Canonical relative paths are clean and slash-separated. +func pathRel(workDir, dir string) (abs, canonical string) { + if filepath.IsAbs(dir) { + abs = filepath.Clean(dir) + return abs, abs + } + + abs = filepath.Join(base.Cwd(), dir) + rel, err := filepath.Rel(workDir, abs) + if err != nil { + // The path can't be made relative to the go.work file, + // so it must be kept absolute instead. + return abs, abs + } + + // Normalize relative paths to use slashes, so that checked-in go.work + // files with relative paths within the repo are platform-independent. + return abs, modload.ToDirectoryPath(rel) +} |