Implements output of debug code.

Partially imlpements output of release code.
Reorganizes testdata directory.
pull/4/head
Jim Teeuwen 2013-10-30 02:11:04 +01:00
parent c4d457542a
commit 17f2782db6
15 changed files with 534 additions and 47 deletions

12
asset.go Normal file
View File

@ -0,0 +1,12 @@
// This work is subject to the CC0 1.0 Universal (CC0 1.0) Public Domain Dedication
// license. Its contents can be found at:
// http://creativecommons.org/publicdomain/zero/1.0/
package bindata
// File is an asset entry for the table of contents.
type Asset struct {
Path string // Full file path.
Name string // Key used in TOC -- name by which asset is referenced.
Func string // Function name for the procedure returning the asset contents.
}

37
bytewriter.go Normal file
View File

@ -0,0 +1,37 @@
// This work is subject to the CC0 1.0 Universal (CC0 1.0) Public Domain Dedication
// license. Its contents can be found at:
// http://creativecommons.org/publicdomain/zero/1.0/
package bindata
import (
"fmt"
"io"
)
var newline = []byte{'\n'}
type ByteWriter struct {
io.Writer
c int
}
func (w *ByteWriter) Write(p []byte) (n int, err error) {
if len(p) == 0 {
return
}
for n = range p {
if w.c%12 == 0 {
w.Writer.Write(newline)
w.c = 0
}
fmt.Fprintf(w.Writer, "0x%02x,", p[n])
w.c++
}
n++
return
}

View File

@ -6,11 +6,14 @@ package bindata
// Config defines a set of options for the asset conversion.
type Config struct {
// Name of the package to use. Defaults to 'main'.
Package string
// Tags specify a set of optional build tags, which should be
// included in the generated output. The tags are appended to a
// `// +build` line in the beginning of the output file
// and must follow the build tags syntax specified by the go tool.
Tags []string
Tags string
// Input defines the directory path, containing all asset files.
// This may contain sub directories, which will be included in the
@ -86,7 +89,16 @@ type Config struct {
/*
Compress means the assets are GZIP compressed before being turned
into Go code. The generated function will automatically unzip
the file data when called.
the file data when called. Defaults to true.
*/
Compress bool
}
// NewConfig returns a default configuration struct.
func NewConfig() *Config {
c := new(Config)
c.Package = "main"
c.NoMemCopy = false
c.Compress = true
return c
}

View File

@ -27,32 +27,67 @@ type ProgressFunc func(file string, current, total int) bool
// to Go code and writes new files to the output directory specified
// in the given configuration.
func Translate(c *Config, pf ProgressFunc) error {
toc := make(map[string]string)
err := findFiles(c.Input, c.Prefix, toc)
var toc []Asset
err := findFiles(c.Input, c.Prefix, &toc)
if err != nil {
return err
}
// Open output files.
debug, release, err := openOutput(c.Output)
if err != nil {
return err
}
defer func() {
debug.Close()
release.Close()
}()
// Prepare files -- write package header and build tags.
writeDebugHeader(debug, c)
writeReleaseHeader(release, c)
// Convert assets and write them to the output files.
var current int
for key, value := range toc {
for i := range toc {
if pf != nil {
current++
if pf(key, current, len(toc)) {
if pf(toc[i].Path, current, len(toc)) {
return nil
}
}
_ = value
writeDebug(debug, c, &toc[i])
writeRelease(release, c, &toc[i])
}
// Generate TOC file.
return nil
}
// openOutput opens two output files. One for debug code and
// one for release code.
func openOutput(dir string) (*os.File, *os.File, error) {
debug, err := os.Create(filepath.Join(dir, "bindata_debug.go"))
if err != nil {
return nil, nil, err
}
release, err := os.Create(filepath.Join(dir, "bindata_release.go"))
if err != nil {
debug.Close()
return nil, nil, err
}
return debug, release, nil
}
// fillTOC recursively finds all the file paths in the given directory tree.
// They are added to the given map as keys. Values will be safe function names
// for each file, which will be used when generating the output code.
func findFiles(dir, prefix string, toc map[string]string) error {
func findFiles(dir, prefix string, toc *[]Asset) error {
if len(prefix) > 0 {
dir, _ = filepath.Abs(dir)
prefix, _ = filepath.Abs(prefix)
@ -71,28 +106,32 @@ func findFiles(dir, prefix string, toc map[string]string) error {
}
for _, file := range list {
key := filepath.Join(dir, file.Name())
var asset Asset
asset.Path = filepath.Join(dir, file.Name())
asset.Name = asset.Path
if file.IsDir() {
findFiles(key, prefix, toc)
} else {
if strings.HasPrefix(key, prefix) {
key = key[len(prefix):]
}
// If we have a leading slash, get rid of it.
if len(key) > 0 && key[0] == '/' {
key = key[1:]
}
// This shouldn't happen.
if len(key) == 0 {
return fmt.Errorf("Invalid file: %v", filepath.Join(dir, file.Name()))
}
value := safeFunctionName(key)
toc[key] = value
findFiles(asset.Path, prefix, toc)
continue
}
if strings.HasPrefix(asset.Name, prefix) {
asset.Name = asset.Name[len(prefix):]
}
// If we have a leading slash, get rid of it.
if len(asset.Name) > 0 && asset.Name[0] == '/' {
asset.Name = asset.Name[1:]
}
// This shouldn't happen.
if len(asset.Name) == 0 {
return fmt.Errorf("Invalid file: %v", asset.Path)
}
asset.Func = safeFunctionName(asset.Name)
asset.Path, _ = filepath.Abs(asset.Path)
*toc = append(*toc, asset)
}
return nil
@ -106,15 +145,20 @@ func safeFunctionName(name string) string {
name = strings.ToLower(name)
name = regFuncName.ReplaceAllString(name, "_")
// Identifier can't start with a digit.
if unicode.IsDigit(rune(name[0])) {
name = "_" + name
}
// Get rid of "__" instances for niceness.
for strings.Index(name, "__") > -1 {
name = strings.Replace(name, "__", "_", -1)
}
// Leading underscores are silly (unless they prefix a digit (see below)).
for len(name) > 1 && name[0] == '_' {
name = name[1:]
}
// Identifier can't start with a digit.
if unicode.IsDigit(rune(name[0])) {
name = "_" + name
}
return name
}

69
debug.go Normal file
View File

@ -0,0 +1,69 @@
// This work is subject to the CC0 1.0 Universal (CC0 1.0) Public Domain Dedication
// license. Its contents can be found at:
// http://creativecommons.org/publicdomain/zero/1.0/
package bindata
import (
"fmt"
"io"
)
// writeDebugHeader writes output file headers with the given build tags.
// This targets debug builds.
func writeDebugHeader(w io.Writer, c *Config) {
// Write build tags, if applicable.
if len(c.Tags) > 0 {
fmt.Fprintf(w, "// +build !release %s\n\n", c.Tags)
} else {
fmt.Fprintf(w, "// +build !release\n\n")
}
// Write package declaration
fmt.Fprintf(w, "package %s\n\n", c.Package)
// Define packages we need to import.
// And add the asset_read function. This is called
// from asset-specific functions.
fmt.Fprintf(w, `import (
"bytes"
"io"
"log"
"os"
)
// bindata_read reads the given file from disk.
// It panics if anything went wrong.
func bindata_read(path, name string) []byte {
fd, err := os.Open(path)
if err != nil {
log.Fatalf("Read %%s: %%v", name, err)
}
defer fd.Close()
var buf bytes.Buffer
_, err = io.Copy(&buf, fd)
if err != nil {
log.Fatalf("Read %%s: %%v", name, err)
}
return buf.Bytes()
}
`)
}
// writeDebug write a debug entry for the given asset.
// A debug entry is simply a function which reads the asset from
// the original file (e.g.: from disk).
func writeDebug(w io.Writer, c *Config, asset *Asset) {
fmt.Fprintf(w, `func %s() []byte {
return bindata_read(
%q,
%q,
)
}
`, asset.Func, asset.Path, asset.Name)
}

View File

@ -10,7 +10,6 @@ import (
"github.com/jteeuwen/go-bindata"
"os"
"path/filepath"
"strings"
)
func main() {
@ -29,19 +28,19 @@ func main() {
// any of the command line options are incorrect.
func parseArgs() (*bindata.Config, bindata.ProgressFunc) {
var version, quiet bool
var tagstr string
c := new(bindata.Config)
c := bindata.NewConfig()
flag.Usage = func() {
fmt.Printf("Usage: %s [options] <input> [<output>]\n\n", os.Args[0])
flag.PrintDefaults()
}
flag.StringVar(&tagstr, "tags", "", "Comma-separated list of build tags to include.")
flag.StringVar(&c.Prefix, "prefix", "", "Optional path prefix to strip off map keys and function names.")
flag.BoolVar(&c.NoMemCopy, "nomemcopy", false, "Use a .rodata hack to get rid of unnecessary memcopies. Refer to the documentation to see what implications this carries.")
flag.BoolVar(&c.Compress, "compress", false, "Assets will be GZIP compressed when this flag is specified.")
flag.StringVar(&c.Tags, "tags", c.Tags, "Comma-separated list of build tags to include.")
flag.StringVar(&c.Prefix, "prefix", c.Prefix, "Optional path prefix to strip off map keys and function names.")
flag.StringVar(&c.Package, "pkg", c.Package, "Package name to use in the generated code.")
flag.BoolVar(&c.NoMemCopy, "nomemcopy", c.NoMemCopy, "Use a .rodata hack to get rid of unnecessary memcopies. Refer to the documentation to see what implications this carries.")
flag.BoolVar(&c.Compress, "compress", c.Compress, "Assets will be GZIP compressed when this flag is specified.")
flag.BoolVar(&version, "version", false, "Displays version information.")
flag.BoolVar(&quiet, "quiet", false, "Do not print conversion status.")
flag.Parse()
@ -102,13 +101,6 @@ func parseArgs() (*bindata.Config, bindata.ProgressFunc) {
}
}
// Process build tags.
if len(tagstr) > 0 {
c.Tags = strings.Split(tagstr, ",")
} else {
c.Tags = append(c.Tags, "debug")
}
if quiet {
return c, nil
}

View File

@ -10,7 +10,7 @@ import (
)
const (
AppName = "bindata"
AppName = "go-bindata"
AppVersionMajor = 3
AppVersionMinor = 1
)

215
release.go Normal file
View File

@ -0,0 +1,215 @@
// This work is subject to the CC0 1.0 Universal (CC0 1.0) Public Domain Dedication
// license. Its contents can be found at:
// http://creativecommons.org/publicdomain/zero/1.0/
package bindata
import (
"compress/gzip"
"fmt"
"io"
"os"
)
// writeReleaseHeader writes output file headers with the given build tags.
// This targets release builds.
func writeReleaseHeader(w io.Writer, c *Config) {
// Write build tags, if applicable.
if len(c.Tags) > 0 {
fmt.Fprintf(w, "// +build release %s\n\n", c.Tags)
} else {
fmt.Fprintf(w, "// +build release\n\n")
}
// Write package declaration
fmt.Fprintf(w, "package %s\n\n", c.Package)
if c.Compress {
if c.NoMemCopy {
header_compressed_nomemcopy(w)
} else {
header_compressed_memcopy(w)
}
} else {
if c.NoMemCopy {
header_uncompressed_nomemcopy(w)
} else {
header_uncompressed_memcopy(w)
}
}
}
// writeRelease write a release entry for the given asset.
// A release entry is a function which embeds and returns
// the file's byte content.
func writeRelease(w io.Writer, c *Config, asset *Asset) error {
fd, err := os.Open(asset.Path)
if err != nil {
return err
}
defer fd.Close()
if c.Compress {
if c.NoMemCopy {
compressed_nomemcopy(w, asset, fd)
} else {
compressed_memcopy(w, asset, fd)
}
} else {
if c.NoMemCopy {
uncompressed_nomemcopy(w, asset, fd)
} else {
uncompressed_memcopy(w, asset, fd)
}
}
return nil
}
func header_compressed_nomemcopy(w io.Writer) {
fmt.Fprintf(w, `
import (
"bytes"
"compress/gzip"
"io"
"log"
"reflect"
"unsafe"
)
func bindata_read(data, name string) []byte {
var empty [0]byte
sx := (*reflect.StringHeader)(unsafe.Pointer(&data))
b := empty[:]
bx := (*reflect.SliceHeader)(unsafe.Pointer(&b))
bx.Data = sx.Data
bx.Len = len(data)
bx.Cap = bx.Len
gz, err := gzip.NewReader(bytes.NewBuffer(b))
if err != nil {
log.Fatalf("Read %%q: %%v", name, err)
}
var buf bytes.Buffer
_, err = io.Copy(&buf, gz)
gz.Close()
if err != nil {
log.Fatalf("Read %%q: %%v", name, err)
}
return buf.Bytes()
}
`)
}
func header_compressed_memcopy(w io.Writer) {
fmt.Fprintf(w, `
import (
"bytes"
"compress/gzip"
"io"
"log"
)
func bindata_read(data []byte, name string) []byte {
gz, err := gzip.NewReader(bytes.NewBuffer(data))
if err != nil {
log.Fatalf("Read %%q: %%v", name, err)
}
var buf bytes.Buffer
_, err = io.Copy(&buf, gz)
gz.Close()
if err != nil {
log.Fatalf("Read %%q: %%v", name, err)
}
return buf.Bytes()
}
`)
}
func header_uncompressed_nomemcopy(w io.Writer) {
fmt.Fprintf(w, `
import (
"log"
"reflect"
"unsafe"
)
func bindata_read(data, name string) []byte {
var empty [0]byte
sx := (*reflect.StringHeader)(unsafe.Pointer(&data))
b := empty[:]
bx := (*reflect.SliceHeader)(unsafe.Pointer(&b))
bx.Data = sx.Data
bx.Len = len(data)
bx.Cap = bx.Len
return b
}
`)
}
func header_uncompressed_memcopy(w io.Writer) {
// nop -- We require no imports or helper functions.
}
func compressed_nomemcopy(w io.Writer, asset *Asset, r io.Reader) {
fmt.Fprintf(w, `var _%s = "`, asset.Func)
gz := gzip.NewWriter(&StringWriter{Writer: w})
io.Copy(gz, r)
gz.Close()
fmt.Fprintf(w, `"
func %s() []byte {
return bindata_read(
_%s,
%q,
)
}
`, asset.Func, asset.Func, asset.Name)
}
func compressed_memcopy(w io.Writer, asset *Asset, r io.Reader) {
fmt.Fprintf(w, `func %s() []byte {
return bindata_read([]byte{`, asset.Func)
gz := gzip.NewWriter(&ByteWriter{Writer: w})
io.Copy(gz, r)
gz.Close()
fmt.Fprintf(w, `
},
%q,
)
}
`, asset.Name)
}
func uncompressed_nomemcopy(w io.Writer, asset *Asset, r io.Reader) {
}
func uncompressed_memcopy(w io.Writer, asset *Asset, r io.Reader) {
fmt.Fprintf(w, `func %s() []byte {
return []byte{`, asset.Func)
io.Copy(&ByteWriter{Writer: w}, r)
fmt.Fprintf(w, `
}
}
`)
}

30
stringwriter.go Normal file
View File

@ -0,0 +1,30 @@
// This work is subject to the CC0 1.0 Universal (CC0 1.0) Public Domain Dedication
// license. Its contents can be found at:
// http://creativecommons.org/publicdomain/zero/1.0/
package bindata
import (
"fmt"
"io"
)
type StringWriter struct {
io.Writer
c int
}
func (w *StringWriter) Write(p []byte) (n int, err error) {
if len(p) == 0 {
return
}
for n = range p {
fmt.Fprintf(w.Writer, "\\x%02x", p[n])
w.c++
}
n++
return
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

1
testdata/in/c/test.asset vendored Normal file
View File

@ -0,0 +1 @@
// sample file

51
testdata/out/bindata_debug.go vendored Normal file
View File

@ -0,0 +1,51 @@
// +build !release
package main
import (
"bytes"
"io"
"log"
"os"
)
// bindata_read reads the given file from disk.
// It panics if anything went wrong.
func bindata_read(path, name string) []byte {
fd, err := os.Open(path)
if err != nil {
log.Fatalf("Read %s: %v", name, err)
}
defer fd.Close()
var buf bytes.Buffer
_, err = io.Copy(&buf, fd)
if err != nil {
log.Fatalf("Read %s: %v", name, err)
}
return buf.Bytes()
}
func testdata_in_b_test_asset() []byte {
return bindata_read(
"/a/code/go/src/github.com/jteeuwen/go-bindata/testdata/in/b/test.asset",
"../testdata/in/b/test.asset",
)
}
func testdata_in_a_test_asset() []byte {
return bindata_read(
"/a/code/go/src/github.com/jteeuwen/go-bindata/testdata/in/a/test.asset",
"../testdata/in/a/test.asset",
)
}
func testdata_in_c_test_asset() []byte {
return bindata_read(
"/a/code/go/src/github.com/jteeuwen/go-bindata/testdata/in/c/test.asset",
"../testdata/in/c/test.asset",
)
}

24
testdata/out/bindata_release.go vendored Normal file
View File

@ -0,0 +1,24 @@
// +build release
package main
func testdata_in_b_test_asset() []byte {
return []byte{
0x2f,0x2f,0x20,0x73,0x61,0x6d,0x70,0x6c,0x65,0x20,0x66,0x69,
0x6c,0x65,0x0a,
}
}
func testdata_in_a_test_asset() []byte {
return []byte{
0x73,0x61,0x6d,0x70,0x6c,0x65,0x20,0x66,0x69,0x6c,0x65,0x0a,
}
}
func testdata_in_c_test_asset() []byte {
return []byte{
0x2f,0x2f,0x20,0x73,0x61,0x6d,0x70,0x6c,0x65,0x20,0x66,0x69,
0x6c,0x65,0x0a,
}
}