diff --git a/cmd/cloner/cloner.go b/cmd/cloner/cloner.go index b72cd1ea2..41d036eec 100644 --- a/cmd/cloner/cloner.go +++ b/cmd/cloner/cloner.go @@ -18,15 +18,14 @@ import ( "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) } } diff --git a/util/codegen/codegen.go b/util/codegen/codegen.go new file mode 100644 index 000000000..b7fbbc073 --- /dev/null +++ b/util/codegen/codegen.go @@ -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 +}