golang.org/x/tools/gopls@v0.15.3/internal/vulncheck/copier.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  //go:build ignore
     6  // +build ignore
     7  
     8  //go:generate go run ./copier.go
     9  
    10  // Copier is a tool to automate copy of govulncheck's internal files.
    11  //
    12  //   - copy golang.org/x/vuln/internal/osv/ to osv
    13  //   - copy golang.org/x/vuln/internal/govulncheck/ to govulncheck
    14  package main
    15  
    16  import (
    17  	"bytes"
    18  	"encoding/json"
    19  	"fmt"
    20  	"go/parser"
    21  	"go/token"
    22  	"log"
    23  	"os"
    24  	"os/exec"
    25  	"path/filepath"
    26  	"strconv"
    27  	"strings"
    28  
    29  	"golang.org/x/tools/internal/edit"
    30  )
    31  
    32  func main() {
    33  	log.SetPrefix("copier: ")
    34  	log.SetFlags(log.Lshortfile)
    35  
    36  	srcMod := "golang.org/x/vuln"
    37  	srcModVers := "@latest"
    38  	srcDir, srcVer := downloadModule(srcMod + srcModVers)
    39  
    40  	cfg := rewrite{
    41  		banner:        fmt.Sprintf("// Code generated by copying from %v@%v (go run copier.go); DO NOT EDIT.", srcMod, srcVer),
    42  		srcImportPath: "golang.org/x/vuln/internal",
    43  		dstImportPath: currentPackagePath(),
    44  	}
    45  
    46  	copyFiles("osv", filepath.Join(srcDir, "internal", "osv"), cfg)
    47  	copyFiles("govulncheck", filepath.Join(srcDir, "internal", "govulncheck"), cfg)
    48  }
    49  
    50  type rewrite struct {
    51  	// DO NOT EDIT marker to add at the beginning
    52  	banner string
    53  	// rewrite srcImportPath with dstImportPath
    54  	srcImportPath string
    55  	dstImportPath string
    56  }
    57  
    58  func copyFiles(dst, src string, cfg rewrite) {
    59  	entries, err := os.ReadDir(src)
    60  	if err != nil {
    61  		log.Fatalf("failed to read dir: %v", err)
    62  	}
    63  	if err := os.MkdirAll(dst, 0777); err != nil {
    64  		log.Fatalf("failed to create dir: %v", err)
    65  	}
    66  
    67  	for _, e := range entries {
    68  		fname := e.Name()
    69  		// we need only non-test go files.
    70  		if e.IsDir() || !strings.HasSuffix(fname, ".go") || strings.HasSuffix(fname, "_test.go") {
    71  			continue
    72  		}
    73  		data, err := os.ReadFile(filepath.Join(src, fname))
    74  		if err != nil {
    75  			log.Fatal(err)
    76  		}
    77  		fset := token.NewFileSet()
    78  		f, err := parser.ParseFile(fset, fname, data, parser.ParseComments|parser.ImportsOnly)
    79  		if err != nil {
    80  			log.Fatalf("parsing source module:\n%s", err)
    81  		}
    82  
    83  		buf := edit.NewBuffer(data)
    84  		at := func(p token.Pos) int {
    85  			return fset.File(p).Offset(p)
    86  		}
    87  
    88  		// Add banner right after the copyright statement (the first comment)
    89  		bannerInsert, banner := f.FileStart, cfg.banner
    90  		if len(f.Comments) > 0 && strings.HasPrefix(f.Comments[0].Text(), "Copyright ") {
    91  			bannerInsert = f.Comments[0].End()
    92  			banner = "\n\n" + banner
    93  		}
    94  		buf.Replace(at(bannerInsert), at(bannerInsert), banner)
    95  
    96  		// Adjust imports
    97  		for _, spec := range f.Imports {
    98  			path, err := strconv.Unquote(spec.Path.Value)
    99  			if err != nil {
   100  				log.Fatal(err)
   101  			}
   102  			if strings.HasPrefix(path, cfg.srcImportPath) {
   103  				newPath := strings.Replace(path, cfg.srcImportPath, cfg.dstImportPath, 1)
   104  				buf.Replace(at(spec.Path.Pos()), at(spec.Path.End()), strconv.Quote(newPath))
   105  			}
   106  		}
   107  		data = buf.Bytes()
   108  
   109  		if err := os.WriteFile(filepath.Join(dst, fname), data, 0666); err != nil {
   110  			log.Fatal(err)
   111  		}
   112  	}
   113  }
   114  
   115  func downloadModule(srcModVers string) (dir, ver string) {
   116  	var stdout, stderr bytes.Buffer
   117  	cmd := exec.Command("go", "mod", "download", "-json", srcModVers)
   118  	cmd.Stdout = &stdout
   119  	cmd.Stderr = &stderr
   120  	if err := cmd.Run(); err != nil {
   121  		log.Fatalf("go mod download -json %s: %v\n%s%s", srcModVers, err, stderr.Bytes(), stdout.Bytes())
   122  	}
   123  	var info struct {
   124  		Dir     string
   125  		Version string
   126  	}
   127  	if err := json.Unmarshal(stdout.Bytes(), &info); err != nil {
   128  		log.Fatalf("go mod download -json %s: invalid JSON output: %v\n%s%s", srcModVers, err, stderr.Bytes(), stdout.Bytes())
   129  	}
   130  	return info.Dir, info.Version
   131  }
   132  
   133  func currentPackagePath() string {
   134  	var stdout, stderr bytes.Buffer
   135  	cmd := exec.Command("go", "list", ".")
   136  	cmd.Stdout = &stdout
   137  	cmd.Stderr = &stderr
   138  	if err := cmd.Run(); err != nil {
   139  		log.Fatalf("go list: %v\n%s%s", err, stderr.Bytes(), stdout.Bytes())
   140  	}
   141  	return strings.TrimSpace(stdout.String())
   142  }