github.com/bir3/gocompiler@v0.3.205/src/cmd/gocmd/internal/workcmd/use.go (about) 1 // Copyright 2021 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 // go work use 6 7 package workcmd 8 9 import ( 10 "github.com/bir3/gocompiler/src/cmd/gocmd/internal/base" 11 "github.com/bir3/gocompiler/src/cmd/gocmd/internal/fsys" 12 "github.com/bir3/gocompiler/src/cmd/gocmd/internal/modload" 13 "github.com/bir3/gocompiler/src/cmd/gocmd/internal/str" 14 "context" 15 "fmt" 16 "io/fs" 17 "os" 18 "path/filepath" 19 ) 20 21 var cmdUse = &base.Command{ 22 UsageLine: "go work use [-r] moddirs", 23 Short: "add modules to workspace file", 24 Long: `Use provides a command-line interface for adding 25 directories, optionally recursively, to a go.work file. 26 27 A use directive will be added to the go.work file for each argument 28 directory listed on the command line go.work file, if it exists on disk, 29 or removed from the go.work file if it does not exist on disk. 30 31 The -r flag searches recursively for modules in the argument 32 directories, and the use command operates as if each of the directories 33 were specified as arguments: namely, use directives will be added for 34 directories that exist, and removed for directories that do not exist. 35 36 See the workspaces reference at https://go.dev/ref/mod#workspaces 37 for more information. 38 `, 39 } 40 41 var useR = cmdUse.Flag.Bool("r", false, "") 42 43 func init() { 44 cmdUse.Run = runUse // break init cycle 45 46 base.AddChdirFlag(&cmdUse.Flag) 47 base.AddModCommonFlags(&cmdUse.Flag) 48 } 49 50 func runUse(ctx context.Context, cmd *base.Command, args []string) { 51 modload.ForceUseModules = true 52 53 var gowork string 54 modload.InitWorkfile() 55 gowork = modload.WorkFilePath() 56 57 if gowork == "" { 58 base.Fatalf("go: no go.work file found\n\t(run 'go work init' first or specify path using GOWORK environment variable)") 59 } 60 workFile, err := modload.ReadWorkFile(gowork) 61 if err != nil { 62 base.Fatalf("go: %v", err) 63 } 64 workDir := filepath.Dir(gowork) // Absolute, since gowork itself is absolute. 65 66 haveDirs := make(map[string][]string) // absolute → original(s) 67 for _, use := range workFile.Use { 68 var abs string 69 if filepath.IsAbs(use.Path) { 70 abs = filepath.Clean(use.Path) 71 } else { 72 abs = filepath.Join(workDir, use.Path) 73 } 74 haveDirs[abs] = append(haveDirs[abs], use.Path) 75 } 76 77 // keepDirs maps each absolute path to keep to the literal string to use for 78 // that path (either an absolute or a relative path), or the empty string if 79 // all entries for the absolute path should be removed. 80 keepDirs := make(map[string]string) 81 82 // lookDir updates the entry in keepDirs for the directory dir, 83 // which is either absolute or relative to the current working directory 84 // (not necessarily the directory containing the workfile). 85 lookDir := func(dir string) { 86 absDir, dir := pathRel(workDir, dir) 87 88 fi, err := fsys.Stat(filepath.Join(absDir, "go.mod")) 89 if err != nil { 90 if os.IsNotExist(err) { 91 keepDirs[absDir] = "" 92 } else { 93 base.Errorf("go: %v", err) 94 } 95 return 96 } 97 98 if !fi.Mode().IsRegular() { 99 base.Errorf("go: %v is not regular", filepath.Join(dir, "go.mod")) 100 } 101 102 if dup := keepDirs[absDir]; dup != "" && dup != dir { 103 base.Errorf(`go: already added "%s" as "%s"`, dir, dup) 104 } 105 keepDirs[absDir] = dir 106 } 107 108 if len(args) == 0 { 109 base.Fatalf("go: 'go work use' requires one or more directory arguments") 110 } 111 for _, useDir := range args { 112 absArg, _ := pathRel(workDir, useDir) 113 114 info, err := fsys.Stat(absArg) 115 if err != nil { 116 // Errors raised from os.Stat are formatted to be more user-friendly. 117 if os.IsNotExist(err) { 118 base.Errorf("go: directory %v does not exist", absArg) 119 } else { 120 base.Errorf("go: %v", err) 121 } 122 continue 123 } else if !info.IsDir() { 124 base.Errorf("go: %s is not a directory", absArg) 125 continue 126 } 127 128 if !*useR { 129 lookDir(useDir) 130 continue 131 } 132 133 // Add or remove entries for any subdirectories that still exist. 134 fsys.Walk(useDir, func(path string, info fs.FileInfo, err error) error { 135 if err != nil { 136 return err 137 } 138 139 if !info.IsDir() { 140 if info.Mode()&fs.ModeSymlink != 0 { 141 if target, err := fsys.Stat(path); err == nil && target.IsDir() { 142 fmt.Fprintf(os.Stderr, "warning: ignoring symlink %s\n", path) 143 } 144 } 145 return nil 146 } 147 lookDir(path) 148 return nil 149 }) 150 151 // Remove entries for subdirectories that no longer exist. 152 // Because they don't exist, they will be skipped by Walk. 153 for absDir := range haveDirs { 154 if str.HasFilePathPrefix(absDir, absArg) { 155 if _, ok := keepDirs[absDir]; !ok { 156 keepDirs[absDir] = "" // Mark for deletion. 157 } 158 } 159 } 160 } 161 162 base.ExitIfErrors() 163 164 for absDir, keepDir := range keepDirs { 165 nKept := 0 166 for _, dir := range haveDirs[absDir] { 167 if dir == keepDir { // (note that dir is always non-empty) 168 nKept++ 169 } else { 170 workFile.DropUse(dir) 171 } 172 } 173 if keepDir != "" && nKept != 1 { 174 // If we kept more than one copy, delete them all. 175 // We'll recreate a unique copy with AddUse. 176 if nKept > 1 { 177 workFile.DropUse(keepDir) 178 } 179 workFile.AddUse(keepDir, "") 180 } 181 } 182 modload.UpdateWorkFile(workFile) 183 modload.WriteWorkFile(gowork, workFile) 184 } 185 186 // pathRel returns the absolute and canonical forms of dir for use in a 187 // go.work file located in directory workDir. 188 // 189 // If dir is relative, it is intepreted relative to base.Cwd() 190 // and its canonical form is relative to workDir if possible. 191 // If dir is absolute or cannot be made relative to workDir, 192 // its canonical form is absolute. 193 // 194 // Canonical absolute paths are clean. 195 // Canonical relative paths are clean and slash-separated. 196 func pathRel(workDir, dir string) (abs, canonical string) { 197 if filepath.IsAbs(dir) { 198 abs = filepath.Clean(dir) 199 return abs, abs 200 } 201 202 abs = filepath.Join(base.Cwd(), dir) 203 rel, err := filepath.Rel(workDir, abs) 204 if err != nil { 205 // The path can't be made relative to the go.work file, 206 // so it must be kept absolute instead. 207 return abs, abs 208 } 209 210 // Normalize relative paths to use slashes, so that checked-in go.work 211 // files with relative paths within the repo are platform-independent. 212 return abs, modload.ToDirectoryPath(rel) 213 }