You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
228 lines
6.0 KiB
228 lines
6.0 KiB
// The bindata command embeds binary files as byte arrays into a Go source file.
|
|
//
|
|
// It is designed with go generate in mind, but can be used on its own as well.
|
|
//
|
|
// The data is stored as a map of byte slices or strings indexed by the
|
|
// file paths as specified on the command line. The default name of the
|
|
// map is "bindata" but a custom name can be specified on the command line (-m).
|
|
//
|
|
// Multiple files and directories can be provided on the command line.
|
|
// Directories are treated recursively. The keys of the map are the paths
|
|
// of the files relative to the current directory. A different root for
|
|
// the paths can be specified on the command line (-r).
|
|
//
|
|
// By default, the data are saved as byte slices.
|
|
// It is also possible to save them a strings (-s).
|
|
//
|
|
// By default, the package name of the file containing the generate directive
|
|
// is used as the package name of the generated file, or "main" otherwise.
|
|
// A custom package name can also be specified on the command line (-p).
|
|
//
|
|
// The output file can be specified on the command line (-o).
|
|
// If a file already exists at this location, it will be overwritten.
|
|
// The file produced is properly formatted and commented.
|
|
// If no output file is specified, the contents are printed on the standard output.
|
|
//
|
|
// To see the full list of flags, run:
|
|
// bindata -h
|
|
//
|
|
// Example
|
|
//
|
|
// Given a file hello.go containing:
|
|
//
|
|
// package main
|
|
//
|
|
// import "fmt"
|
|
//
|
|
// func main() {
|
|
// fmt.Println("Hello, 世界")
|
|
// }
|
|
//
|
|
// Running `bindata hello.go` will produce:
|
|
//
|
|
// package main
|
|
//
|
|
// // This file is generated. Do not edit directly.
|
|
//
|
|
// // bindata stores binary files as byte slices indexed by filepaths.
|
|
// var bindata = map[string][]byte{
|
|
// "hello.go": []byte{
|
|
// 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x20, 0x6d, 0x61, 0x69, 0x6e,
|
|
// 0x0a, 0x0a, 0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x20, 0x22, 0x66, 0x6d,
|
|
// 0x74, 0x22, 0x0a, 0x0a, 0x66, 0x75, 0x6e, 0x63, 0x20, 0x6d, 0x61, 0x69,
|
|
// 0x6e, 0x28, 0x29, 0x20, 0x7b, 0x0a, 0x09, 0x66, 0x6d, 0x74, 0x2e, 0x50,
|
|
// 0x72, 0x69, 0x6e, 0x74, 0x6c, 0x6e, 0x28, 0x22, 0x48, 0x65, 0x6c, 0x6c,
|
|
// 0x6f, 0x2c, 0x20, 0xe4, 0xb8, 0x96, 0xe7, 0x95, 0x8c, 0x22, 0x29, 0x0a,
|
|
// 0x7d, 0x0a,
|
|
// },
|
|
// }
|
|
//
|
|
// Example using go generate
|
|
//
|
|
// Add a command like this one anywhere in a source file:
|
|
// //go:generate bindata -o jpegs.go pic1.jpg pic2.jpg pic3.jpg
|
|
// Then simply run
|
|
// go generate
|
|
// and the file jpegs.go will be created.
|
|
package main
|
|
|
|
import (
|
|
"bufio"
|
|
"flag"
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"path/filepath"
|
|
"text/template"
|
|
)
|
|
|
|
// tmpl is the template of the generated Go source file.
|
|
var tmpl = template.Must(template.New("bindata").Parse(`package {{.Pkg}}
|
|
|
|
// This file is generated. Do not edit directly.
|
|
|
|
// {{.Map}} stores binary files as {{if .AsString}}strings{{else}}byte slices{{end}} indexed by file paths.
|
|
var {{.Map}} = map[string]{{if .AsString}}string{{else}}[]byte{{end}}{{"{"}}{{range $name, $data := .Files}}
|
|
{{printf "%#v" $name}}: {{printf "%#v" $data}},{{end}}
|
|
}
|
|
`))
|
|
|
|
// vars contains the variables required by the template.
|
|
var vars struct {
|
|
Pkg string
|
|
Map string
|
|
AsString bool
|
|
Files map[string]fmt.Formatter
|
|
}
|
|
|
|
func main() {
|
|
if err := run(); err != nil {
|
|
fmt.Println("bindata:", err)
|
|
os.Exit(1)
|
|
}
|
|
}
|
|
|
|
// run executes the program.
|
|
func run() error {
|
|
// use GOPACKAGE (set by go generate) as default package name if available
|
|
pkg := os.Getenv("GOPACKAGE")
|
|
if pkg == "" {
|
|
pkg = "main"
|
|
}
|
|
|
|
var out, prefix string
|
|
fs := flag.NewFlagSet("bindata", flag.ExitOnError)
|
|
fs.StringVar(&out, "o", "", "output file (default: stdout)")
|
|
fs.StringVar(&vars.Pkg, "p", pkg, "name of the package")
|
|
fs.StringVar(&vars.Map, "m", "bindata", "name of the map variable")
|
|
fs.StringVar(&prefix, "r", "", "root path for map keys")
|
|
fs.BoolVar(&vars.AsString, "s", false, "save data as strings")
|
|
if err := fs.Parse(os.Args[1:]); err != nil {
|
|
return err
|
|
}
|
|
|
|
vars.Files = make(map[string]fmt.Formatter)
|
|
for _, path := range fs.Args() {
|
|
if err := AddPath(path, prefix); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
var file *os.File
|
|
if out != "" {
|
|
var err error
|
|
if file, err = os.Create(out); err != nil {
|
|
return err
|
|
}
|
|
} else {
|
|
file = os.Stdout
|
|
}
|
|
|
|
return tmpl.Execute(file, vars)
|
|
}
|
|
|
|
// AddPath add files to the slice in vars recursively.
|
|
func AddPath(path, prefix string) error {
|
|
fi, err := os.Stat(path)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if fi.IsDir() {
|
|
dir, err := os.Open(path)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
files, err := dir.Readdirnames(0)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
for _, file := range files {
|
|
if err := AddPath(filepath.Join(path, file), prefix); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
} else {
|
|
file, err := os.Open(path)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
path, err := filepath.Rel(prefix, path)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if vars.AsString {
|
|
vars.Files[path] = StringFormatter{file}
|
|
} else {
|
|
vars.Files[path] = ByteSliceFormatter{file}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// A ByteSliceFormatter is a byte slice pretty printing io.Reader.
|
|
type ByteSliceFormatter struct {
|
|
io.Reader
|
|
}
|
|
|
|
// Format pretty prints the bytes read from the ByteSliceFormatter.
|
|
func (f ByteSliceFormatter) Format(s fmt.State, c rune) {
|
|
buf := bufio.NewReader(f)
|
|
|
|
const cols = 12 // number of columns in the formatted byte slice.
|
|
|
|
fmt.Fprintf(s, "[]byte{")
|
|
b, err := buf.ReadByte()
|
|
for i := 0; err == nil; i++ {
|
|
if i%cols == 0 {
|
|
fmt.Fprintf(s, "\n\t\t")
|
|
} else {
|
|
fmt.Fprintf(s, " ")
|
|
}
|
|
fmt.Fprintf(s, "%#02x,", b)
|
|
b, err = buf.ReadByte()
|
|
}
|
|
fmt.Fprintf(s, "\n\t}")
|
|
}
|
|
|
|
// A StringFormatter is a string pretty printing io.Reader.
|
|
type StringFormatter struct {
|
|
io.Reader
|
|
}
|
|
|
|
// Format pretty prints the bytes read from the StringFormatter.
|
|
func (f StringFormatter) Format(s fmt.State, c rune) {
|
|
buf := bufio.NewReader(f)
|
|
|
|
const cols = 16 // number of bytes per line in the formatted string.
|
|
|
|
fmt.Fprintf(s, `"`)
|
|
b, err := buf.ReadByte()
|
|
for i := 0; err == nil; i++ {
|
|
if i%cols == 0 {
|
|
fmt.Fprintf(s, "\" +\n\t\t\"")
|
|
}
|
|
fmt.Fprintf(s, "\\x%02x", b)
|
|
b, err = buf.ReadByte()
|
|
}
|
|
fmt.Fprintf(s, `"`)
|
|
}
|