golang.org/x/tools@v0.21.1-0.20240520172518-788d39e776b1/internal/robustio/copyfiles.go (about)

     1  // Copyright 2022 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  //go:build ignore
     6  // +build ignore
     7  
     8  // The copyfiles script copies the contents of the internal cmd/go robustio
     9  // package to the current directory, with adjustments to make it build.
    10  //
    11  // NOTE: In retrospect this script got out of hand, as we have to perform
    12  // various operations on the package to get it to build at old Go versions. If
    13  // in the future it proves to be flaky, delete it and just copy code manually.
    14  package main
    15  
    16  import (
    17  	"bytes"
    18  	"go/build/constraint"
    19  	"go/scanner"
    20  	"go/token"
    21  	"log"
    22  	"os"
    23  	"path/filepath"
    24  	"runtime"
    25  	"strings"
    26  )
    27  
    28  func main() {
    29  	dir := filepath.Join(runtime.GOROOT(), "src", "cmd", "go", "internal", "robustio")
    30  
    31  	entries, err := os.ReadDir(dir)
    32  	if err != nil {
    33  		log.Fatalf("reading the robustio dir: %v", err)
    34  	}
    35  
    36  	// Collect file content so that we can validate before copying.
    37  	fileContent := make(map[string][]byte)
    38  	windowsImport := []byte("\t\"internal/syscall/windows\"\n")
    39  	foundWindowsImport := false
    40  	for _, entry := range entries {
    41  		if strings.HasSuffix(entry.Name(), ".go") {
    42  			pth := filepath.Join(dir, entry.Name())
    43  			content, err := os.ReadFile(pth)
    44  			if err != nil {
    45  				log.Fatalf("reading %q: %v", entry.Name(), err)
    46  			}
    47  
    48  			// Replace the use of internal/syscall/windows.ERROR_SHARING_VIOLATION
    49  			// with a local constant.
    50  			if entry.Name() == "robustio_windows.go" && bytes.Contains(content, windowsImport) {
    51  				foundWindowsImport = true
    52  				content = bytes.Replace(content, windowsImport, nil, 1)
    53  				content = bytes.Replace(content, []byte("windows.ERROR_SHARING_VIOLATION"), []byte("ERROR_SHARING_VIOLATION"), -1)
    54  			}
    55  
    56  			// Replace os.ReadFile with os.ReadFile (for 1.15 and older). We
    57  			// attempt to match calls (via the '('), to avoid matching mentions of
    58  			// os.ReadFile in comments.
    59  			//
    60  			// TODO(rfindley): once we (shortly!) no longer support 1.15, remove
    61  			// this and break the build.
    62  			if bytes.Contains(content, []byte("os.ReadFile(")) {
    63  				content = bytes.Replace(content, []byte("\"os\""), []byte("\"io/ioutil\"\n\t\"os\""), 1)
    64  				content = bytes.Replace(content, []byte("os.ReadFile("), []byte("os.ReadFile("), -1)
    65  			}
    66  
    67  			// Add +build constraints, for 1.16.
    68  			content = addPlusBuildConstraints(content)
    69  
    70  			fileContent[entry.Name()] = content
    71  		}
    72  	}
    73  
    74  	if !foundWindowsImport {
    75  		log.Fatal("missing expected import of internal/syscall/windows in robustio_windows.go")
    76  	}
    77  
    78  	for name, content := range fileContent {
    79  		if err := os.WriteFile(name, content, 0644); err != nil {
    80  			log.Fatalf("writing %q: %v", name, err)
    81  		}
    82  	}
    83  }
    84  
    85  // addPlusBuildConstraints splices in +build constraints for go:build
    86  // constraints encountered in the source.
    87  //
    88  // Gopls still builds at Go 1.16, which requires +build constraints.
    89  func addPlusBuildConstraints(src []byte) []byte {
    90  	var s scanner.Scanner
    91  	fset := token.NewFileSet()
    92  	file := fset.AddFile("", fset.Base(), len(src))
    93  	s.Init(file, src, nil /* no error handler */, scanner.ScanComments)
    94  
    95  	result := make([]byte, 0, len(src))
    96  	lastInsertion := 0
    97  	for {
    98  		pos, tok, lit := s.Scan()
    99  		if tok == token.EOF {
   100  			break
   101  		}
   102  		if tok == token.COMMENT {
   103  			if c, err := constraint.Parse(lit); err == nil {
   104  				plusBuild, err := constraint.PlusBuildLines(c)
   105  				if err != nil {
   106  					log.Fatalf("computing +build constraint for %q: %v", lit, err)
   107  				}
   108  				insertAt := file.Offset(pos) + len(lit)
   109  				result = append(result, src[lastInsertion:insertAt]...)
   110  				result = append(result, []byte("\n"+strings.Join(plusBuild, "\n"))...)
   111  				lastInsertion = insertAt
   112  			}
   113  		}
   114  	}
   115  	result = append(result, src[lastInsertion:]...)
   116  	return result
   117  }