// Copyright 2020 The Kubernetes Authors. // SPDX-License-Identifier: Apache-2.0 // A code copier launched via `go generate`. // See k8segen/doc.go for further discussion. package main import ( "bufio" "bytes" "fmt" "log" "os" "os/exec" "path/filepath" "strings" ) const ( // Splitting this so it doesn't show up in grep. apiMachineryModule = "k8s.io" + "/" + "apimachinery" apiMachineryVersion = "v0.17.0" sigsK8sIo = "sigs.k8s.io" ) var ( filesToCopy = map[string][]string{ filepath.Join("pkg", "labels"): { "labels.go", "selector.go", "zz_generated.deepcopy.go", }, filepath.Join("pkg", "selection"): { "operator.go", }, filepath.Join("pkg", "util", "sets"): { "empty.go", "string.go", }, filepath.Join("pkg", "util", "errors"): { "errors.go", }, filepath.Join("pkg", "util", "validation"): { "validation.go", }, filepath.Join("pkg", "util", "validation", "field"): { "errors.go", "path.go", }, } ) func main() { c := newCopier() // c.print() runNoOutputCommand("go", "get", c.apiMachineryModSpec()) for dir, files := range filesToCopy { for _, n := range files { if err := c.copyFile(dir, n); err != nil { log.Fatal(err) } } } runNoOutputCommand( "go", "mod", "edit", "-droprequire="+apiMachineryModule) runNoOutputCommand("go", "mod", "tidy") runGetOutputCommand("go", "fmt", "./...") } type copier struct { goModCache string topPackage string srcDir string pgmName string } func (c copier) apiMachineryModSpec() string { return apiMachineryModule + "@" + apiMachineryVersion } func (c copier) replacementPath() string { return filepath.Join(c.topPackage, c.subPath()) } func (c copier) subPath() string { return filepath.Join("internal", c.pgmName) } func (c copier) print() { fmt.Printf(" apiMachineryModule: %s\n", apiMachineryModule) fmt.Printf(" replacementPath: %s\n", c.replacementPath()) fmt.Printf(" goModCache: %s\n", c.goModCache) fmt.Printf(" topPackage: %s\n", c.topPackage) fmt.Printf(" subPath: %s\n", c.subPath()) fmt.Printf(" srcDir: %s\n", c.srcDir) fmt.Printf(" apiMachineryModSpec: %s\n", c.apiMachineryModSpec()) fmt.Printf(" pgmName: %s\n", c.pgmName) fmt.Printf(" pwd: %s\n", os.Getenv("PWD")) } func newCopier() copier { tmp := copier{ pgmName: os.Getenv("GOPACKAGE"), goModCache: runGetOutputCommand("go", "env", "GOMODCACHE"), } goMod := runGetOutputCommand("go", "env", "GOMOD") topPackage := filepath.Join(goMod[:len(goMod)-len("go.mod")-1], "yaml") k := strings.Index(topPackage, sigsK8sIo) if k < 1 { log.Fatalf("cannot find %s in %s", sigsK8sIo, topPackage) } tmp.srcDir = topPackage[:k-1] tmp.topPackage = topPackage[k:] return tmp } func (c copier) copyFile(dir, name string) error { inFile, err := os.Open( filepath.Join(c.goModCache, c.apiMachineryModSpec(), dir, name)) if err != nil { return err } defer inFile.Close() scanner := bufio.NewScanner(inFile) w, err := newWriter(dir, name) if err != nil { return err } defer w.close() w.write( fmt.Sprintf( // This particular phrasing is required. "// Code generated by %s/generator from %s; DO NOT EDIT.", c.pgmName, c.apiMachineryModSpec())) w.write( fmt.Sprintf( "// Copied from %s\n", filepath.Join(c.apiMachineryModSpec(), dir, name))) for scanner.Scan() { l := scanner.Text() // Disallow recursive generation. if strings.HasPrefix(l, "//go:generate") { continue } // Don't want it to appear double generated. if strings.HasPrefix(l, "// Code generated") { continue } // Fix self-imports. l = strings.Replace(l, apiMachineryModule, c.replacementPath(), 1) // Replace klog with generic log (eschewing k8s.io entirely). l = strings.Replace(l, "\"k8s.io/klog\"", "\"log\"", 1) l = strings.Replace(l, "klog.V(10).Infof(", "log.Printf(", 1) w.write(l) } if err := scanner.Err(); err != nil { return err } w.write("") return nil } type writer struct { root string f *os.File } func newWriter(toDir, name string) (*writer, error) { if err := os.MkdirAll(toDir, 0755); err != nil { log.Printf("unable to create directory: %s", toDir) return nil, err } n := filepath.Join(toDir, name) f, err := os.Create(n) if err != nil { return nil, fmt.Errorf("unable to create `%s`; %v", n, err) } return &writer{root: toDir, f: f}, nil } func (w *writer) close() { w.f.Close() } func (w *writer) write(line string) { if _, err := w.f.WriteString(line + "\n"); err != nil { log.Printf("Trouble writing: %s", line) log.Fatalf("Error: %s", err) } } func runNoOutputCommand(n string, args ...string) { o := runGetOutputCommand(n, args...) if len(o) > 0 { log.Fatalf("unexpected output: %q", o) } } func runGetOutputCommand(n string, args ...string) string { cmd := exec.Command(n, args...) var outBuf bytes.Buffer cmd.Stdout = &outBuf var errBuf bytes.Buffer cmd.Stderr = &errBuf if err := cmd.Run(); err != nil { fmt.Printf("err: %q\n", errBuf.String()) log.Fatal(err) } return strings.TrimSpace(outBuf.String()) }