Files
avo/attr/make_textflag.go

208 lines
4.2 KiB
Go
Raw Normal View History

//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)
}
}