//go:build ignore // +build ignore package main import ( "bufio" "bytes" "errors" "flag" "fmt" "go/format" "io" "io/ioutil" "log" "net/http" "os" "path/filepath" "runtime" "strconv" "strings" ) var ( TextFlagPath = "src/runtime/textflag.h" TextFlagName = filepath.Base(TextFlagPath) ) var ( download = flag.Bool("download", false, "download new version of "+TextFlagName) version = flag.String("version", runtime.Version(), "go version to download file from") textflag = flag.String("textflag", TextFlagName, "path to "+TextFlagName) output = flag.String("output", "", "path to output file") ) func main() { if err := mainerr(); err != nil { log.Fatal(err) } } func mainerr() error { flag.Parse() // Download new version, if requested. if *download { if err := DownloadGoSourceFile(*textflag, *version, TextFlagPath); err != nil { return err } log.Printf("downloaded %q from version %s to %q", TextFlagPath, *version, *textflag) } // Parse text flags header. fs, err := ParseFile(*textflag) if err != nil { return err } // Determine output. w := os.Stdout if *output != "" { f, err := os.Create(*output) if err != nil { return err } defer f.Close() w = f } // Generate code and format it. buf := bytes.NewBuffer(nil) PrintFlagAttributes(buf, fs) src, err := format.Source(buf.Bytes()) if err != nil { return err } // Write output. _, err = w.Write(src) if err != nil { return err } return nil } // DownloadGoSourceFile downloads a Go source file from a specific version. func DownloadGoSourceFile(outpath, v, srcpath string) (err error) { // Download from github. url := "https://github.com/golang/go/raw/" + v + "/" + srcpath res, err := http.Get(url) if err != nil { return err } defer func() { if errc := res.Body.Close(); errc != nil && err == nil { err = errc } }() // Write to file. buf := bytes.NewBuffer(nil) fmt.Fprintf(buf, "// Code generated by downloading from %s. DO NOT EDIT.\n\n", url) if _, err := io.Copy(buf, res.Body); err != nil { return err } if err := ioutil.WriteFile(outpath, buf.Bytes(), 0o644); err != nil { return err } return nil } type Flag struct { Doc []string Name string Value int } // Parse text flags header format. func Parse(r io.Reader) ([]Flag, error) { var fs []Flag var doc []string s := bufio.NewScanner(r) for s.Scan() { line := s.Text() switch { case strings.Contains(line, "TODO"): // skip case strings.HasPrefix(line, "// "): doc = append(doc, strings.TrimPrefix(line, "// ")) case strings.HasPrefix(line, "#define"): fields := strings.Fields(line) if len(fields) != 3 || fields[0] != "#define" { return nil, fmt.Errorf("unexpected line format %q", line) } v, err := strconv.Atoi(fields[2]) if err != nil { return nil, err } fs = append(fs, Flag{ Doc: doc, Name: fields[1], Value: v, }) doc = nil case line == "" || line == "//": doc = nil default: return nil, errors.New("unexpected format") } } if err := s.Err(); err != nil { return nil, err } return fs, nil } // ParseFile parses text flags header file. func ParseFile(filename string) ([]Flag, error) { b, err := ioutil.ReadFile(filename) if err != nil { return nil, err } return Parse(bytes.NewReader(b)) } func PrintFlagAttributes(w io.Writer, fs []Flag) { _, self, _, _ := runtime.Caller(0) fmt.Fprintf(w, "// Code generated by %s. DO NOT EDIT.\n\n", filepath.Base(self)) fmt.Fprintf(w, "package attr\n") // Attribute constants. fmt.Fprintf(w, "\n// Attribute values defined in %s.\n", TextFlagName) fmt.Fprintf(w, "const (\n") for _, f := range fs { for _, line := range f.Doc { fmt.Fprintf(w, "\t// %s\n", line) } fmt.Fprintf(w, "\t%s Attribute = %d\n\n", f.Name, f.Value) } fmt.Fprintf(w, ")\n") // String map. fmt.Fprintf(w, "\nvar attrname = map[Attribute]string{\n") for _, f := range fs { fmt.Fprintf(w, "\t%s: %q,\n", f.Name, f.Name) } fmt.Fprintf(w, "}\n") // Flag test methods. for _, f := range fs { fmt.Fprintf(w, "\n// %s reports whether the %s flag is set.\n", f.Name, f.Name) fmt.Fprintf(w, "func (a Attribute) %s() bool { return (a & %s) != 0 }\n", f.Name, f.Name) } }