util/codegen: add package

This is a package for shared utilities used in doing codegen programs.
The inaugural API is for writing gofmt'd code to a file.

Signed-off-by: Josh Bleecher Snyder <josh@tailscale.com>
This commit is contained in:
Josh Bleecher Snyder 2021-09-16 15:41:57 -07:00 committed by Josh Bleecher Snyder
parent 4da559d7cc
commit fb66ff7c78
2 changed files with 42 additions and 8 deletions

View File

@ -18,15 +18,14 @@
"flag"
"fmt"
"go/ast"
"go/format"
"go/token"
"go/types"
"io/ioutil"
"log"
"os"
"strings"
"golang.org/x/tools/go/packages"
"tailscale.com/util/codegen"
)
var (
@ -130,17 +129,12 @@ func main() {
fmt.Fprintf(contents, ")\n\n")
contents.Write(buf.Bytes())
out, err := format.Source(contents.Bytes())
if err != nil {
log.Fatalf("%s, in source:\n%s", err, contents.Bytes())
}
output := *flagOutput
if output == "" {
flag.Usage()
os.Exit(2)
}
if err := ioutil.WriteFile(output, out, 0644); err != nil {
if err := codegen.WriteFormatted(contents.Bytes(), output); err != nil {
log.Fatal(err)
}
}

40
util/codegen/codegen.go Normal file
View File

@ -0,0 +1,40 @@
// Copyright (c) 2021 Tailscale Inc & AUTHORS All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package codegen contains shared utilities for generating code.
package codegen
import (
"fmt"
"go/format"
"os"
)
// WriteFormatted writes code to path.
// It runs gofmt on it before writing;
// if gofmt fails, it writes code unchanged.
// Errors can include I/O errors and gofmt errors.
//
// The advantage of always writing code to path,
// even if gofmt fails, is that it makes debugging easier.
// The code can be long, but you need it in order to debug.
// It is nicer to work with it in a file than a terminal.
// It is also easier to interpret gofmt errors
// with an editor providing file and line numbers.
func WriteFormatted(code []byte, path string) error {
out, fmterr := format.Source(code)
if fmterr != nil {
out = code
}
ioerr := os.WriteFile(path, out, 0644)
// Prefer I/O errors. They're usually easier to fix,
// and until they're fixed you can't do much else.
if ioerr != nil {
return ioerr
}
if fmterr != nil {
return fmt.Errorf("%s:%v", path, fmterr)
}
return nil
}