// 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, `"`) }