attr: generate code from textflag.h (#176)
This commit is contained in:
committed by
GitHub
parent
e5c9b4e5a6
commit
c5faaae583
200
attr/make_textflag.go
Normal file
200
attr/make_textflag.go
Normal file
@@ -0,0 +1,200 @@
|
||||
// +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(), 0644); 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")
|
||||
}
|
||||
Reference in New Issue
Block a user