libgo: update to Go 1.11
[official-gcc.git] / libgo / go / cmd / go / internal / modfetch / fetch.go
blob2e26bac434da8d17a03c006bb770f636d1d1a397
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.
5 package modfetch
7 import (
8 "archive/zip"
9 "bytes"
10 "fmt"
11 "io"
12 "io/ioutil"
13 "os"
14 "path/filepath"
15 "sort"
16 "strings"
17 "sync"
19 "cmd/go/internal/base"
20 "cmd/go/internal/cfg"
21 "cmd/go/internal/dirhash"
22 "cmd/go/internal/module"
23 "cmd/go/internal/par"
26 var downloadCache par.Cache
28 // Download downloads the specific module version to the
29 // local download cache and returns the name of the directory
30 // corresponding to the root of the module's file tree.
31 func Download(mod module.Version) (dir string, err error) {
32 if PkgMod == "" {
33 // Do not download to current directory.
34 return "", fmt.Errorf("missing modfetch.PkgMod")
37 // The par.Cache here avoids duplicate work but also
38 // avoids conflicts from simultaneous calls by multiple goroutines
39 // for the same version.
40 type cached struct {
41 dir string
42 err error
44 c := downloadCache.Do(mod, func() interface{} {
45 dir, err := DownloadDir(mod)
46 if err != nil {
47 return cached{"", err}
49 if files, _ := ioutil.ReadDir(dir); len(files) == 0 {
50 zipfile, err := DownloadZip(mod)
51 if err != nil {
52 return cached{"", err}
54 modpath := mod.Path + "@" + mod.Version
55 if err := Unzip(dir, zipfile, modpath, 0); err != nil {
56 fmt.Fprintf(os.Stderr, "-> %s\n", err)
57 return cached{"", err}
60 checkSum(mod)
61 return cached{dir, nil}
62 }).(cached)
63 return c.dir, c.err
66 var downloadZipCache par.Cache
68 // DownloadZip downloads the specific module version to the
69 // local zip cache and returns the name of the zip file.
70 func DownloadZip(mod module.Version) (zipfile string, err error) {
71 // The par.Cache here avoids duplicate work but also
72 // avoids conflicts from simultaneous calls by multiple goroutines
73 // for the same version.
74 type cached struct {
75 zipfile string
76 err error
78 c := downloadZipCache.Do(mod, func() interface{} {
79 zipfile, err := CachePath(mod, "zip")
80 if err != nil {
81 return cached{"", err}
83 if _, err := os.Stat(zipfile); err == nil {
84 // Use it.
85 // This should only happen if the mod/cache directory is preinitialized
86 // or if pkg/mod/path was removed but not pkg/mod/cache/download.
87 if cfg.CmdName != "mod download" {
88 fmt.Fprintf(os.Stderr, "go: extracting %s %s\n", mod.Path, mod.Version)
90 } else {
91 if err := os.MkdirAll(filepath.Dir(zipfile), 0777); err != nil {
92 return cached{"", err}
94 if cfg.CmdName != "mod download" {
95 fmt.Fprintf(os.Stderr, "go: downloading %s %s\n", mod.Path, mod.Version)
97 if err := downloadZip(mod, zipfile); err != nil {
98 return cached{"", err}
101 return cached{zipfile, nil}
102 }).(cached)
103 return c.zipfile, c.err
106 func downloadZip(mod module.Version, target string) error {
107 repo, err := Lookup(mod.Path)
108 if err != nil {
109 return err
111 tmpfile, err := repo.Zip(mod.Version, os.TempDir())
112 if err != nil {
113 return err
115 defer os.Remove(tmpfile)
117 // Double-check zip file looks OK.
118 z, err := zip.OpenReader(tmpfile)
119 if err != nil {
120 return err
122 prefix := mod.Path + "@" + mod.Version
123 for _, f := range z.File {
124 if !strings.HasPrefix(f.Name, prefix) {
125 z.Close()
126 return fmt.Errorf("zip for %s has unexpected file %s", prefix[:len(prefix)-1], f.Name)
129 z.Close()
131 hash, err := dirhash.HashZip(tmpfile, dirhash.DefaultHash)
132 if err != nil {
133 return err
135 checkOneSum(mod, hash) // check before installing the zip file
136 r, err := os.Open(tmpfile)
137 if err != nil {
138 return err
140 defer r.Close()
141 w, err := os.Create(target)
142 if err != nil {
143 return err
145 if _, err := io.Copy(w, r); err != nil {
146 w.Close()
147 return fmt.Errorf("copying: %v", err)
149 if err := w.Close(); err != nil {
150 return err
152 return ioutil.WriteFile(target+"hash", []byte(hash), 0666)
155 var GoSumFile string // path to go.sum; set by package modload
157 var goSum struct {
158 mu sync.Mutex
159 m map[module.Version][]string // content of go.sum file (+ go.modverify if present)
160 enabled bool // whether to use go.sum at all
161 modverify string // path to go.modverify, to be deleted
164 // initGoSum initializes the go.sum data.
165 // It reports whether use of go.sum is now enabled.
166 // The goSum lock must be held.
167 func initGoSum() bool {
168 if GoSumFile == "" {
169 return false
171 if goSum.m != nil {
172 return true
175 goSum.m = make(map[module.Version][]string)
176 data, err := ioutil.ReadFile(GoSumFile)
177 if err != nil && !os.IsNotExist(err) {
178 base.Fatalf("go: %v", err)
180 goSum.enabled = true
181 readGoSum(GoSumFile, data)
183 // Add old go.modverify file.
184 // We'll delete go.modverify in WriteGoSum.
185 alt := strings.TrimSuffix(GoSumFile, ".sum") + ".modverify"
186 if data, err := ioutil.ReadFile(alt); err == nil {
187 readGoSum(alt, data)
188 goSum.modverify = alt
190 return true
193 // emptyGoModHash is the hash of a 1-file tree containing a 0-length go.mod.
194 // A bug caused us to write these into go.sum files for non-modules.
195 // We detect and remove them.
196 const emptyGoModHash = "h1:G7mAYYxgmS0lVkHyy2hEOLQCFB0DlQFTMLWggykrydY="
198 // readGoSum parses data, which is the content of file,
199 // and adds it to goSum.m. The goSum lock must be held.
200 func readGoSum(file string, data []byte) {
201 lineno := 0
202 for len(data) > 0 {
203 var line []byte
204 lineno++
205 i := bytes.IndexByte(data, '\n')
206 if i < 0 {
207 line, data = data, nil
208 } else {
209 line, data = data[:i], data[i+1:]
211 f := strings.Fields(string(line))
212 if len(f) == 0 {
213 // blank line; skip it
214 continue
216 if len(f) != 3 {
217 base.Fatalf("go: malformed go.sum:\n%s:%d: wrong number of fields %v", file, lineno, len(f))
219 if f[2] == emptyGoModHash {
220 // Old bug; drop it.
221 continue
223 mod := module.Version{Path: f[0], Version: f[1]}
224 goSum.m[mod] = append(goSum.m[mod], f[2])
228 // checkSum checks the given module's checksum.
229 func checkSum(mod module.Version) {
230 if PkgMod == "" {
231 // Do not use current directory.
232 return
235 // Do the file I/O before acquiring the go.sum lock.
236 ziphash, err := CachePath(mod, "ziphash")
237 if err != nil {
238 base.Fatalf("go: verifying %s@%s: %v", mod.Path, mod.Version, err)
240 data, err := ioutil.ReadFile(ziphash)
241 if err != nil {
242 if os.IsNotExist(err) {
243 // This can happen if someone does rm -rf GOPATH/src/cache/download. So it goes.
244 return
246 base.Fatalf("go: verifying %s@%s: %v", mod.Path, mod.Version, err)
248 h := strings.TrimSpace(string(data))
249 if !strings.HasPrefix(h, "h1:") {
250 base.Fatalf("go: verifying %s@%s: unexpected ziphash: %q", mod.Path, mod.Version, h)
253 checkOneSum(mod, h)
256 // goModSum returns the checksum for the go.mod contents.
257 func goModSum(data []byte) (string, error) {
258 return dirhash.Hash1([]string{"go.mod"}, func(string) (io.ReadCloser, error) {
259 return ioutil.NopCloser(bytes.NewReader(data)), nil
263 // checkGoMod checks the given module's go.mod checksum;
264 // data is the go.mod content.
265 func checkGoMod(path, version string, data []byte) {
266 h, err := goModSum(data)
267 if err != nil {
268 base.Fatalf("go: verifying %s %s go.mod: %v", path, version, err)
271 checkOneSum(module.Version{Path: path, Version: version + "/go.mod"}, h)
274 // checkOneSum checks that the recorded hash for mod is h.
275 func checkOneSum(mod module.Version, h string) {
276 goSum.mu.Lock()
277 defer goSum.mu.Unlock()
278 if !initGoSum() {
279 return
282 for _, vh := range goSum.m[mod] {
283 if h == vh {
284 return
286 if strings.HasPrefix(vh, "h1:") {
287 base.Fatalf("go: verifying %s@%s: checksum mismatch\n\tdownloaded: %v\n\tgo.sum: %v", mod.Path, mod.Version, h, vh)
290 if len(goSum.m[mod]) > 0 {
291 fmt.Fprintf(os.Stderr, "warning: verifying %s@%s: unknown hashes in go.sum: %v; adding %v", mod.Path, mod.Version, strings.Join(goSum.m[mod], ", "), h)
293 goSum.m[mod] = append(goSum.m[mod], h)
296 // Sum returns the checksum for the downloaded copy of the given module,
297 // if present in the download cache.
298 func Sum(mod module.Version) string {
299 if PkgMod == "" {
300 // Do not use current directory.
301 return ""
304 ziphash, err := CachePath(mod, "ziphash")
305 if err != nil {
306 return ""
308 data, err := ioutil.ReadFile(ziphash)
309 if err != nil {
310 return ""
312 return strings.TrimSpace(string(data))
315 // WriteGoSum writes the go.sum file if it needs to be updated.
316 func WriteGoSum() {
317 goSum.mu.Lock()
318 defer goSum.mu.Unlock()
319 if !initGoSum() {
320 return
323 var mods []module.Version
324 for m := range goSum.m {
325 mods = append(mods, m)
327 module.Sort(mods)
328 var buf bytes.Buffer
329 for _, m := range mods {
330 list := goSum.m[m]
331 sort.Strings(list)
332 for _, h := range list {
333 fmt.Fprintf(&buf, "%s %s %s\n", m.Path, m.Version, h)
337 data, _ := ioutil.ReadFile(GoSumFile)
338 if !bytes.Equal(data, buf.Bytes()) {
339 if err := ioutil.WriteFile(GoSumFile, buf.Bytes(), 0666); err != nil {
340 base.Fatalf("go: writing go.sum: %v", err)
344 if goSum.modverify != "" {
345 os.Remove(goSum.modverify)
349 // TrimGoSum trims go.sum to contain only the modules for which keep[m] is true.
350 func TrimGoSum(keep map[module.Version]bool) {
351 goSum.mu.Lock()
352 defer goSum.mu.Unlock()
353 if !initGoSum() {
354 return
357 for m := range goSum.m {
358 // If we're keeping x@v we also keep x@v/go.mod.
359 // Map x@v/go.mod back to x@v for the keep lookup.
360 noGoMod := module.Version{Path: m.Path, Version: strings.TrimSuffix(m.Version, "/go.mod")}
361 if !keep[m] && !keep[noGoMod] {
362 delete(goSum.m, m)