Source file src/pkg/cmd/go/internal/modcmd/vendor.go
1
2
3
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 "cmd/go/internal/base"
18 "cmd/go/internal/cfg"
19 "cmd/go/internal/imports"
20 "cmd/go/internal/modload"
21 "cmd/go/internal/module"
22 )
23
24 var cmdVendor = &base.Command{
25 UsageLine: "go mod vendor [-v]",
26 Short: "make vendored copy of dependencies",
27 Long: `
28 Vendor resets the main module's vendor directory to include all packages
29 needed to build and test all the main module's packages.
30 It does not include test code for vendored packages.
31
32 The -v flag causes vendor to print the names of vendored
33 modules and packages to standard error.
34 `,
35 Run: runVendor,
36 }
37
38 func init() {
39 cmdVendor.Flag.BoolVar(&cfg.BuildV, "v", false, "")
40 }
41
42 func runVendor(cmd *base.Command, args []string) {
43 if len(args) != 0 {
44 base.Fatalf("go mod vendor: vendor takes no arguments")
45 }
46 pkgs := modload.LoadVendor()
47
48 vdir := filepath.Join(modload.ModRoot(), "vendor")
49 if err := os.RemoveAll(vdir); err != nil {
50 base.Fatalf("go mod vendor: %v", err)
51 }
52
53 modpkgs := make(map[module.Version][]string)
54 for _, pkg := range pkgs {
55 m := modload.PackageModule(pkg)
56 if m == modload.Target {
57 continue
58 }
59 modpkgs[m] = append(modpkgs[m], pkg)
60 }
61
62 var buf bytes.Buffer
63 for _, m := range modload.BuildList()[1:] {
64 if pkgs := modpkgs[m]; len(pkgs) > 0 {
65 repl := ""
66 if r := modload.Replacement(m); r.Path != "" {
67 repl = " => " + r.Path
68 if r.Version != "" {
69 repl += " " + r.Version
70 }
71 }
72 fmt.Fprintf(&buf, "# %s %s%s\n", m.Path, m.Version, repl)
73 if cfg.BuildV {
74 fmt.Fprintf(os.Stderr, "# %s %s%s\n", m.Path, m.Version, repl)
75 }
76 sort.Strings(pkgs)
77 for _, pkg := range pkgs {
78 fmt.Fprintf(&buf, "%s\n", pkg)
79 if cfg.BuildV {
80 fmt.Fprintf(os.Stderr, "%s\n", pkg)
81 }
82 vendorPkg(vdir, pkg)
83 }
84 }
85 }
86 if buf.Len() == 0 {
87 fmt.Fprintf(os.Stderr, "go: no dependencies to vendor\n")
88 return
89 }
90 if err := ioutil.WriteFile(filepath.Join(vdir, "modules.txt"), buf.Bytes(), 0666); err != nil {
91 base.Fatalf("go mod vendor: %v", err)
92 }
93 }
94
95 func vendorPkg(vdir, pkg string) {
96 realPath := modload.ImportMap(pkg)
97 if realPath != pkg && modload.ImportMap(realPath) != "" {
98 fmt.Fprintf(os.Stderr, "warning: %s imported as both %s and %s; making two copies.\n", realPath, realPath, pkg)
99 }
100
101 dst := filepath.Join(vdir, pkg)
102 src := modload.PackageDir(realPath)
103 if src == "" {
104 fmt.Fprintf(os.Stderr, "internal error: no pkg for %s -> %s\n", pkg, realPath)
105 }
106 copyDir(dst, src, matchPotentialSourceFile)
107 if m := modload.PackageModule(realPath); m.Path != "" {
108 copyMetadata(m.Path, realPath, dst, src)
109 }
110 }
111
112 type metakey struct {
113 modPath string
114 dst string
115 }
116
117 var copiedMetadata = make(map[metakey]bool)
118
119
120
121 func copyMetadata(modPath, pkg, dst, src string) {
122 for parent := 0; ; parent++ {
123 if copiedMetadata[metakey{modPath, dst}] {
124 break
125 }
126 copiedMetadata[metakey{modPath, dst}] = true
127 if parent > 0 {
128 copyDir(dst, src, matchMetadata)
129 }
130 if modPath == pkg {
131 break
132 }
133 pkg = filepath.Dir(pkg)
134 dst = filepath.Dir(dst)
135 src = filepath.Dir(src)
136 }
137 }
138
139
140
141
142
143
144
145
146 var metaPrefixes = []string{
147 "AUTHORS",
148 "CONTRIBUTORS",
149 "COPYLEFT",
150 "COPYING",
151 "COPYRIGHT",
152 "LEGAL",
153 "LICENSE",
154 "NOTICE",
155 "PATENTS",
156 }
157
158
159 func matchMetadata(dir string, info os.FileInfo) bool {
160 name := info.Name()
161 for _, p := range metaPrefixes {
162 if strings.HasPrefix(name, p) {
163 return true
164 }
165 }
166 return false
167 }
168
169
170 func matchPotentialSourceFile(dir string, info os.FileInfo) bool {
171 if strings.HasSuffix(info.Name(), "_test.go") {
172 return false
173 }
174 if strings.HasSuffix(info.Name(), ".go") {
175 f, err := os.Open(filepath.Join(dir, info.Name()))
176 if err != nil {
177 base.Fatalf("go mod vendor: %v", err)
178 }
179 defer f.Close()
180
181 content, err := imports.ReadImports(f, false, nil)
182 if err == nil && !imports.ShouldBuild(content, imports.AnyTags()) {
183
184
185 return false
186 }
187 return true
188 }
189
190
191
192 return true
193 }
194
195
196 func copyDir(dst, src string, match func(dir string, info os.FileInfo) bool) {
197 files, err := ioutil.ReadDir(src)
198 if err != nil {
199 base.Fatalf("go mod vendor: %v", err)
200 }
201 if err := os.MkdirAll(dst, 0777); err != nil {
202 base.Fatalf("go mod vendor: %v", err)
203 }
204 for _, file := range files {
205 if file.IsDir() || !file.Mode().IsRegular() || !match(src, file) {
206 continue
207 }
208 r, err := os.Open(filepath.Join(src, file.Name()))
209 if err != nil {
210 base.Fatalf("go mod vendor: %v", err)
211 }
212 w, err := os.Create(filepath.Join(dst, file.Name()))
213 if err != nil {
214 base.Fatalf("go mod vendor: %v", err)
215 }
216 if _, err := io.Copy(w, r); err != nil {
217 base.Fatalf("go mod vendor: %v", err)
218 }
219 r.Close()
220 if err := w.Close(); err != nil {
221 base.Fatalf("go mod vendor: %v", err)
222 }
223 }
224 }
225
View as plain text