github.com/gagliardetto/golang-go@v0.0.0-20201020153340-53909ea70814/cmd/go/not-internal/modcmd/vendor.go (about) 1 // Copyright 2018 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 package modcmd 6 7 import ( 8 "bytes" 9 "fmt" 10 "io" 11 "io/ioutil" 12 "os" 13 "path/filepath" 14 "sort" 15 "strings" 16 17 "github.com/gagliardetto/golang-go/cmd/go/not-internal/base" 18 "github.com/gagliardetto/golang-go/cmd/go/not-internal/cfg" 19 "github.com/gagliardetto/golang-go/cmd/go/not-internal/imports" 20 "github.com/gagliardetto/golang-go/cmd/go/not-internal/modload" 21 "github.com/gagliardetto/golang-go/cmd/go/not-internal/work" 22 23 "golang.org/x/mod/module" 24 "golang.org/x/mod/semver" 25 ) 26 27 var cmdVendor = &base.Command{ 28 UsageLine: "go mod vendor [-v]", 29 Short: "make vendored copy of dependencies", 30 Long: ` 31 Vendor resets the main module's vendor directory to include all packages 32 needed to build and test all the main module's packages. 33 It does not include test code for vendored packages. 34 35 The -v flag causes vendor to print the names of vendored 36 modules and packages to standard error. 37 `, 38 Run: runVendor, 39 } 40 41 func init() { 42 cmdVendor.Flag.BoolVar(&cfg.BuildV, "v", false, "") 43 work.AddModCommonFlags(cmdVendor) 44 } 45 46 func runVendor(cmd *base.Command, args []string) { 47 if len(args) != 0 { 48 base.Fatalf("go mod vendor: vendor takes no arguments") 49 } 50 pkgs := modload.LoadVendor() 51 52 vdir := filepath.Join(modload.ModRoot(), "vendor") 53 if err := os.RemoveAll(vdir); err != nil { 54 base.Fatalf("go mod vendor: %v", err) 55 } 56 57 modpkgs := make(map[module.Version][]string) 58 for _, pkg := range pkgs { 59 m := modload.PackageModule(pkg) 60 if m == modload.Target { 61 continue 62 } 63 modpkgs[m] = append(modpkgs[m], pkg) 64 } 65 66 includeAllReplacements := false 67 isExplicit := map[module.Version]bool{} 68 if gv := modload.ModFile().Go; gv != nil && semver.Compare("v"+gv.Version, "v1.14") >= 0 { 69 // If the Go version is at least 1.14, annotate all explicit 'require' and 70 // 'replace' targets found in the go.mod file so that we can perform a 71 // stronger consistency check when -mod=vendor is set. 72 for _, r := range modload.ModFile().Require { 73 isExplicit[r.Mod] = true 74 } 75 includeAllReplacements = true 76 } 77 78 var buf bytes.Buffer 79 for _, m := range modload.BuildList()[1:] { 80 if pkgs := modpkgs[m]; len(pkgs) > 0 || isExplicit[m] { 81 line := moduleLine(m, modload.Replacement(m)) 82 buf.WriteString(line) 83 if cfg.BuildV { 84 os.Stderr.WriteString(line) 85 } 86 if isExplicit[m] { 87 buf.WriteString("## explicit\n") 88 if cfg.BuildV { 89 os.Stderr.WriteString("## explicit\n") 90 } 91 } 92 sort.Strings(pkgs) 93 for _, pkg := range pkgs { 94 fmt.Fprintf(&buf, "%s\n", pkg) 95 if cfg.BuildV { 96 fmt.Fprintf(os.Stderr, "%s\n", pkg) 97 } 98 vendorPkg(vdir, pkg) 99 } 100 } 101 } 102 103 if includeAllReplacements { 104 // Record unused and wildcard replacements at the end of the modules.txt file: 105 // without access to the complete build list, the consumer of the vendor 106 // directory can't otherwise determine that those replacements had no effect. 107 for _, r := range modload.ModFile().Replace { 108 if len(modpkgs[r.Old]) > 0 { 109 // We we already recorded this replacement in the entry for the replaced 110 // module with the packages it provides. 111 continue 112 } 113 114 line := moduleLine(r.Old, r.New) 115 buf.WriteString(line) 116 if cfg.BuildV { 117 os.Stderr.WriteString(line) 118 } 119 } 120 } 121 122 if buf.Len() == 0 { 123 fmt.Fprintf(os.Stderr, "go: no dependencies to vendor\n") 124 return 125 } 126 if err := ioutil.WriteFile(filepath.Join(vdir, "modules.txt"), buf.Bytes(), 0666); err != nil { 127 base.Fatalf("go mod vendor: %v", err) 128 } 129 } 130 131 func moduleLine(m, r module.Version) string { 132 b := new(strings.Builder) 133 b.WriteString("# ") 134 b.WriteString(m.Path) 135 if m.Version != "" { 136 b.WriteString(" ") 137 b.WriteString(m.Version) 138 } 139 if r.Path != "" { 140 b.WriteString(" => ") 141 b.WriteString(r.Path) 142 if r.Version != "" { 143 b.WriteString(" ") 144 b.WriteString(r.Version) 145 } 146 } 147 b.WriteString("\n") 148 return b.String() 149 } 150 151 func vendorPkg(vdir, pkg string) { 152 realPath := modload.ImportMap(pkg) 153 if realPath != pkg && modload.ImportMap(realPath) != "" { 154 fmt.Fprintf(os.Stderr, "warning: %s imported as both %s and %s; making two copies.\n", realPath, realPath, pkg) 155 } 156 157 dst := filepath.Join(vdir, pkg) 158 src := modload.PackageDir(realPath) 159 if src == "" { 160 fmt.Fprintf(os.Stderr, "internal error: no pkg for %s -> %s\n", pkg, realPath) 161 } 162 copyDir(dst, src, matchPotentialSourceFile) 163 if m := modload.PackageModule(realPath); m.Path != "" { 164 copyMetadata(m.Path, realPath, dst, src) 165 } 166 } 167 168 type metakey struct { 169 modPath string 170 dst string 171 } 172 173 var copiedMetadata = make(map[metakey]bool) 174 175 // copyMetadata copies metadata files from parents of src to parents of dst, 176 // stopping after processing the src parent for modPath. 177 func copyMetadata(modPath, pkg, dst, src string) { 178 for parent := 0; ; parent++ { 179 if copiedMetadata[metakey{modPath, dst}] { 180 break 181 } 182 copiedMetadata[metakey{modPath, dst}] = true 183 if parent > 0 { 184 copyDir(dst, src, matchMetadata) 185 } 186 if modPath == pkg { 187 break 188 } 189 pkg = filepath.Dir(pkg) 190 dst = filepath.Dir(dst) 191 src = filepath.Dir(src) 192 } 193 } 194 195 // metaPrefixes is the list of metadata file prefixes. 196 // Vendoring copies metadata files from parents of copied directories. 197 // Note that this list could be arbitrarily extended, and it is longer 198 // in other tools (such as godep or dep). By using this limited set of 199 // prefixes and also insisting on capitalized file names, we are trying 200 // to nudge people toward more agreement on the naming 201 // and also trying to avoid false positives. 202 var metaPrefixes = []string{ 203 "AUTHORS", 204 "CONTRIBUTORS", 205 "COPYLEFT", 206 "COPYING", 207 "COPYRIGHT", 208 "LEGAL", 209 "LICENSE", 210 "NOTICE", 211 "PATENTS", 212 } 213 214 // matchMetadata reports whether info is a metadata file. 215 func matchMetadata(dir string, info os.FileInfo) bool { 216 name := info.Name() 217 for _, p := range metaPrefixes { 218 if strings.HasPrefix(name, p) { 219 return true 220 } 221 } 222 return false 223 } 224 225 // matchPotentialSourceFile reports whether info may be relevant to a build operation. 226 func matchPotentialSourceFile(dir string, info os.FileInfo) bool { 227 if strings.HasSuffix(info.Name(), "_test.go") { 228 return false 229 } 230 if strings.HasSuffix(info.Name(), ".go") { 231 f, err := os.Open(filepath.Join(dir, info.Name())) 232 if err != nil { 233 base.Fatalf("go mod vendor: %v", err) 234 } 235 defer f.Close() 236 237 content, err := imports.ReadImports(f, false, nil) 238 if err == nil && !imports.ShouldBuild(content, imports.AnyTags()) { 239 // The file is explicitly tagged "ignore", so it can't affect the build. 240 // Leave it out. 241 return false 242 } 243 return true 244 } 245 246 // We don't know anything about this file, so optimistically assume that it is 247 // needed. 248 return true 249 } 250 251 // copyDir copies all regular files satisfying match(info) from src to dst. 252 func copyDir(dst, src string, match func(dir string, info os.FileInfo) bool) { 253 files, err := ioutil.ReadDir(src) 254 if err != nil { 255 base.Fatalf("go mod vendor: %v", err) 256 } 257 if err := os.MkdirAll(dst, 0777); err != nil { 258 base.Fatalf("go mod vendor: %v", err) 259 } 260 for _, file := range files { 261 if file.IsDir() || !file.Mode().IsRegular() || !match(src, file) { 262 continue 263 } 264 r, err := os.Open(filepath.Join(src, file.Name())) 265 if err != nil { 266 base.Fatalf("go mod vendor: %v", err) 267 } 268 w, err := os.Create(filepath.Join(dst, file.Name())) 269 if err != nil { 270 base.Fatalf("go mod vendor: %v", err) 271 } 272 if _, err := io.Copy(w, r); err != nil { 273 base.Fatalf("go mod vendor: %v", err) 274 } 275 r.Close() 276 if err := w.Close(); err != nil { 277 base.Fatalf("go mod vendor: %v", err) 278 } 279 } 280 }