golang.org/x/build@v0.0.0-20240506185731-218518f32b70/internal/task/dl2mod.go (about) 1 // Copyright 2023 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 task 6 7 import ( 8 "archive/tar" 9 "archive/zip" 10 "compress/flate" 11 "compress/gzip" 12 "fmt" 13 "io" 14 "strings" 15 "time" 16 17 "golang.org/x/build/internal/releasetargets" 18 ) 19 20 // Converted distributions are treated as versions of golang.org/toolchain. 21 // The archive for go1.2.3.linux-amd64.tar.gz is stored as version v0.0.1-go1.2.3.linux-amd64. 22 const ( 23 modulePath = "golang.org/toolchain" 24 moduleVersion = "v0.0.1" 25 ) 26 27 func ToolchainZipPrefix(target *releasetargets.Target, version string) string { 28 return modulePath + "@" + ToolchainModuleVersion(target, version) 29 } 30 31 func ToolchainModuleVersion(target *releasetargets.Target, version string) string { 32 return fmt.Sprintf("%v-%v.%v-%v", moduleVersion, version, target.GOOS, target.GOARCH) 33 } 34 35 // TarToModFiles converts the distribution archive with the given name and content 36 // to a collection of module files. 37 func TarToModFiles(target *releasetargets.Target, version string, t time.Time, tgz io.Reader, w io.Writer) (mod string, info string, _ error) { 38 vers := ToolchainModuleVersion(target, version) 39 zipPrefix := ToolchainZipPrefix(target, version) 40 41 // rename takes the name of a file found in a distribution archive 42 // and returns the name to use for that file in the module archive. 43 // The main conversion is go/zzz -> golang.org/toolchain@v0.0.1-go<vers>.<goos>-<goarch>/zzz. 44 // If rename returns "", nil, then the file should be omitted from the 45 // module archive entirely. 46 rename := func(name string) (string, error) { 47 if !strings.HasPrefix(name, "go/") { 48 return "", fmt.Errorf("unexpected file name %q", name) 49 } 50 // Modules cannot contain go.mod files, so rename them to _go.mod. 51 if strings.HasSuffix(name, "/go.mod") { 52 name = strings.TrimSuffix(name, "/go.mod") + "/_go.mod" 53 } 54 // Omit these directories. 55 switch { 56 case strings.HasPrefix(name, "go/.github/"), 57 strings.HasPrefix(name, "go/api/"), 58 strings.HasPrefix(name, "go/doc/"), 59 strings.HasPrefix(name, "go/misc/"), 60 strings.HasPrefix(name, "go/test/"): 61 return "", nil 62 } 63 return zipPrefix + name[len("go"):], nil 64 } 65 66 // Convert archive, extracting its modification time for our metadata. 67 zw := zip.NewWriter(w) 68 zw.RegisterCompressor(zip.Deflate, func(out io.Writer) (io.WriteCloser, error) { 69 return flate.NewWriter(out, flate.BestCompression) 70 }) 71 if err := convertTarGz(zw, t, tgz, rename); err == nil { 72 err = zw.Close() 73 } 74 75 info = fmt.Sprintf("{%q:%q, %q:%q}\n", "Version", vers, "Time", t.UTC().Format(time.RFC3339)) 76 mod = fmt.Sprintf("module %s\n", modulePath) 77 return mod, info, nil 78 } 79 80 // convertTarGz writes a distribution .tar.gz archive's content to zw, applying the rename function. 81 func convertTarGz(zw *zip.Writer, t time.Time, tgz io.Reader, rename func(string) (string, error)) error { 82 gzr, err := gzip.NewReader(tgz) 83 if err != nil { 84 return err 85 } 86 defer gzr.Close() 87 88 tr := tar.NewReader(gzr) 89 for { 90 hdr, err := tr.Next() 91 if err != nil { 92 if err == io.EOF { 93 break 94 } 95 return err 96 } 97 if hdr.Typeflag != tar.TypeReg { // omit directories 98 continue 99 } 100 name, err := rename(hdr.Name) 101 if err != nil { 102 return err 103 } 104 if name == "" { // omit files rejected by rename 105 continue 106 } 107 w, err := zw.CreateHeader(&zip.FileHeader{ 108 Name: name, 109 Method: zip.Deflate, 110 Modified: t, 111 }) 112 if err != nil { 113 return err 114 } 115 if _, err := io.Copy(w, tr); err != nil { 116 return err 117 } 118 } 119 return nil 120 }