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  }