| // Copyright 2018 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. |
| |
| package modcmd |
| |
| import ( |
| "bytes" |
| "fmt" |
| "io" |
| "io/ioutil" |
| "os" |
| "path/filepath" |
| "strings" |
| |
| "cmd/go/internal/base" |
| "cmd/go/internal/module" |
| "cmd/go/internal/vgo" |
| ) |
| |
| func runVendor() { |
| pkgs := vgo.LoadVendor() |
| |
| vdir := filepath.Join(vgo.ModRoot, "vendor") |
| if err := os.RemoveAll(vdir); err != nil { |
| base.Fatalf("vgo vendor: %v", err) |
| } |
| |
| modpkgs := make(map[module.Version][]string) |
| for _, pkg := range pkgs { |
| m := vgo.PackageModule(pkg) |
| if m == vgo.Target { |
| continue |
| } |
| modpkgs[m] = append(modpkgs[m], pkg) |
| } |
| |
| var buf bytes.Buffer |
| for _, m := range vgo.BuildList()[1:] { |
| if pkgs := modpkgs[m]; len(pkgs) > 0 { |
| repl := "" |
| if r := vgo.Replacement(m); r.Path != "" { |
| repl = " => " + r.Path |
| if r.Version != "" { |
| repl += " " + r.Version |
| } |
| } |
| fmt.Fprintf(&buf, "# %s %s%s\n", m.Path, m.Version, repl) |
| if *modV { |
| fmt.Fprintf(os.Stderr, "# %s %s%s\n", m.Path, m.Version, repl) |
| } |
| for _, pkg := range pkgs { |
| fmt.Fprintf(&buf, "%s\n", pkg) |
| if *modV { |
| fmt.Fprintf(os.Stderr, "%s\n", pkg) |
| } |
| vendorPkg(vdir, pkg) |
| } |
| } |
| } |
| if buf.Len() == 0 { |
| fmt.Fprintf(os.Stderr, "vgo: no dependencies to vendor\n") |
| return |
| } |
| if err := ioutil.WriteFile(filepath.Join(vdir, "vgo.list"), buf.Bytes(), 0666); err != nil { |
| base.Fatalf("vgo vendor: %v", err) |
| } |
| } |
| |
| func vendorPkg(vdir, pkg string) { |
| realPath := vgo.ImportMap(pkg) |
| if realPath != pkg && vgo.ImportMap(realPath) != "" { |
| fmt.Fprintf(os.Stderr, "warning: %s imported as both %s and %s; making two copies.\n", realPath, realPath, pkg) |
| } |
| |
| dst := filepath.Join(vdir, pkg) |
| src := vgo.PackageDir(realPath) |
| if src == "" { |
| fmt.Fprintf(os.Stderr, "internal error: no pkg for %s -> %s\n", pkg, realPath) |
| } |
| copyDir(dst, src, matchNonTest) |
| if m := vgo.PackageModule(realPath); m.Path != "" { |
| copyMetadata(m.Path, realPath, dst, src) |
| } |
| } |
| |
| type metakey struct { |
| modPath string |
| dst string |
| } |
| |
| var copiedMetadata = make(map[metakey]bool) |
| |
| // copyMetadata copies metadata files from parents of src to parents of dst, |
| // stopping after processing the src parent for modPath. |
| func copyMetadata(modPath, pkg, dst, src string) { |
| for parent := 0; ; parent++ { |
| if copiedMetadata[metakey{modPath, dst}] { |
| break |
| } |
| copiedMetadata[metakey{modPath, dst}] = true |
| if parent > 0 { |
| copyDir(dst, src, matchMetadata) |
| } |
| if modPath == pkg { |
| break |
| } |
| pkg = filepath.Dir(pkg) |
| dst = filepath.Dir(dst) |
| src = filepath.Dir(src) |
| } |
| } |
| |
| // metaPrefixes is the list of metadata file prefixes. |
| // Vendoring copies metadata files from parents of copied directories. |
| // Note that this list could be arbitrarily extended, and it is longer |
| // in other tools (such as godep or dep). By using this limited set of |
| // prefixes and also insisting on capitalized file names, we are trying |
| // to nudge people toward more agreement on the naming |
| // and also trying to avoid false positives. |
| var metaPrefixes = []string{ |
| "AUTHORS", |
| "CONTRIBUTORS", |
| "COPYLEFT", |
| "COPYING", |
| "COPYRIGHT", |
| "LEGAL", |
| "LICENSE", |
| "NOTICE", |
| "PATENTS", |
| } |
| |
| // matchMetadata reports whether info is a metadata file. |
| func matchMetadata(info os.FileInfo) bool { |
| name := info.Name() |
| for _, p := range metaPrefixes { |
| if strings.HasPrefix(name, p) { |
| return true |
| } |
| } |
| return false |
| } |
| |
| // matchNonTest reports whether info is any non-test file (including non-Go files). |
| func matchNonTest(info os.FileInfo) bool { |
| return !strings.HasSuffix(info.Name(), "_test.go") |
| } |
| |
| // copyDir copies all regular files satisfying match(info) from src to dst. |
| func copyDir(dst, src string, match func(os.FileInfo) bool) { |
| files, err := ioutil.ReadDir(src) |
| if err != nil { |
| base.Fatalf("vgo vendor: %v", err) |
| } |
| if err := os.MkdirAll(dst, 0777); err != nil { |
| base.Fatalf("vgo vendor: %v", err) |
| } |
| for _, file := range files { |
| if file.IsDir() || !file.Mode().IsRegular() || !match(file) { |
| continue |
| } |
| r, err := os.Open(filepath.Join(src, file.Name())) |
| if err != nil { |
| base.Fatalf("vgo vendor: %v", err) |
| } |
| w, err := os.Create(filepath.Join(dst, file.Name())) |
| if err != nil { |
| base.Fatalf("vgo vendor: %v", err) |
| } |
| if _, err := io.Copy(w, r); err != nil { |
| base.Fatalf("vgo vendor: %v", err) |
| } |
| r.Close() |
| if err := w.Close(); err != nil { |
| base.Fatalf("vgo vendor: %v", err) |
| } |
| } |
| } |