bindata/release.go
Tobias Schottdorf b5d9eff767 add mode and modTime flags
In some situations, it is desired to have reproducible runs of `go-bindata`
from a fixed git repository commit. Since mtimes and file modes are not
generally versioned, the newly introduced flags aid in that quest by allowing
to completely replace those two pieces of data by something fixed.

Example:
//go:generate go-bindata -pkg resource -mode 0644 -modtime 1388530800 \
                         -o ./embedded.go ./ui/...
2015-03-10 19:17:17 +01:00

391 lines
7.1 KiB
Go

// 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 (
"bytes"
"compress/gzip"
"fmt"
"io"
"io/ioutil"
"os"
"unicode/utf8"
)
// writeRelease writes the release code file.
func writeRelease(w io.Writer, c *Config, toc []Asset) error {
err := writeReleaseHeader(w, c)
if err != nil {
return err
}
for i := range toc {
err = writeReleaseAsset(w, c, &toc[i])
if err != nil {
return err
}
}
return nil
}
// writeReleaseHeader writes output file headers.
// This targets release builds.
func writeReleaseHeader(w io.Writer, c *Config) error {
var err error
if c.NoCompress {
if c.NoMemCopy {
err = header_uncompressed_nomemcopy(w)
} else {
err = header_uncompressed_memcopy(w)
}
} else {
if c.NoMemCopy {
err = header_compressed_nomemcopy(w)
} else {
err = header_compressed_memcopy(w)
}
}
if err != nil {
return err
}
return header_release_common(w)
}
// writeReleaseAsset write a release entry for the given asset.
// A release entry is a function which embeds and returns
// the file's byte content.
func writeReleaseAsset(w io.Writer, c *Config, asset *Asset) error {
fd, err := os.Open(asset.Path)
if err != nil {
return err
}
defer fd.Close()
if c.NoCompress {
if c.NoMemCopy {
err = uncompressed_nomemcopy(w, asset, fd)
} else {
err = uncompressed_memcopy(w, asset, fd)
}
} else {
if c.NoMemCopy {
err = compressed_nomemcopy(w, asset, fd)
} else {
err = compressed_memcopy(w, asset, fd)
}
}
if err != nil {
return err
}
return asset_release_common(w, c, asset)
}
// sanitize prepares a valid UTF-8 string as a raw string constant.
// Based on https://code.google.com/p/go/source/browse/godoc/static/makestatic.go?repo=tools
func sanitize(b []byte) []byte {
// Replace ` with `+"`"+`
b = bytes.Replace(b, []byte("`"), []byte("`+\"`\"+`"), -1)
// Replace BOM with `+"\xEF\xBB\xBF"+`
// (A BOM is valid UTF-8 but not permitted in Go source files.
// I wouldn't bother handling this, but for some insane reason
// jquery.js has a BOM somewhere in the middle.)
return bytes.Replace(b, []byte("\xEF\xBB\xBF"), []byte("`+\"\\xEF\\xBB\\xBF\"+`"), -1)
}
func header_compressed_nomemcopy(w io.Writer) error {
_, err := fmt.Fprintf(w, `import (
"bytes"
"compress/gzip"
"fmt"
"io"
"reflect"
"strings"
"unsafe"
"os"
"time"
"io/ioutil"
"path"
"path/filepath"
)
func bindata_read(data, name string) ([]byte, error) {
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 {
return nil, fmt.Errorf("Read %%q: %%v", name, err)
}
var buf bytes.Buffer
_, err = io.Copy(&buf, gz)
gz.Close()
if err != nil {
return nil, fmt.Errorf("Read %%q: %%v", name, err)
}
return buf.Bytes(), nil
}
`)
return err
}
func header_compressed_memcopy(w io.Writer) error {
_, err := fmt.Fprintf(w, `import (
"bytes"
"compress/gzip"
"fmt"
"io"
"strings"
"os"
"time"
"io/ioutil"
"path"
"path/filepath"
)
func bindata_read(data []byte, name string) ([]byte, error) {
gz, err := gzip.NewReader(bytes.NewBuffer(data))
if err != nil {
return nil, fmt.Errorf("Read %%q: %%v", name, err)
}
var buf bytes.Buffer
_, err = io.Copy(&buf, gz)
gz.Close()
if err != nil {
return nil, fmt.Errorf("Read %%q: %%v", name, err)
}
return buf.Bytes(), nil
}
`)
return err
}
func header_uncompressed_nomemcopy(w io.Writer) error {
_, err := fmt.Fprintf(w, `import (
"fmt"
"reflect"
"strings"
"unsafe"
"os"
"time"
"io/ioutil"
"path"
"path/filepath"
)
func bindata_read(data, name string) ([]byte, error) {
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, nil
}
`)
return err
}
func header_uncompressed_memcopy(w io.Writer) error {
_, err := fmt.Fprintf(w, `import (
"fmt"
"strings"
"os"
"time"
"io/ioutil"
"path"
"path/filepath"
)
`)
return err
}
func header_release_common(w io.Writer) error {
_, err := fmt.Fprintf(w, `type asset struct {
bytes []byte
info os.FileInfo
}
type bindata_file_info struct {
name string
size int64
mode os.FileMode
modTime time.Time
}
func (fi bindata_file_info) Name() string {
return fi.name
}
func (fi bindata_file_info) Size() int64 {
return fi.size
}
func (fi bindata_file_info) Mode() os.FileMode {
return fi.mode
}
func (fi bindata_file_info) ModTime() time.Time {
return fi.modTime
}
func (fi bindata_file_info) IsDir() bool {
return false
}
func (fi bindata_file_info) Sys() interface{} {
return nil
}
`)
return err
}
func compressed_nomemcopy(w io.Writer, asset *Asset, r io.Reader) error {
_, err := fmt.Fprintf(w, `var _%s = "`, asset.Func)
if err != nil {
return err
}
gz := gzip.NewWriter(&StringWriter{Writer: w})
_, err = io.Copy(gz, r)
gz.Close()
if err != nil {
return err
}
_, err = fmt.Fprintf(w, `"
func %s_bytes() ([]byte, error) {
return bindata_read(
_%s,
%q,
)
}
`, asset.Func, asset.Func, asset.Name)
return err
}
func compressed_memcopy(w io.Writer, asset *Asset, r io.Reader) error {
_, err := fmt.Fprintf(w, `var _%s = []byte("`, asset.Func)
if err != nil {
return err
}
gz := gzip.NewWriter(&StringWriter{Writer: w})
_, err = io.Copy(gz, r)
gz.Close()
if err != nil {
return err
}
_, err = fmt.Fprintf(w, `")
func %s_bytes() ([]byte, error) {
return bindata_read(
_%s,
%q,
)
}
`, asset.Func, asset.Func, asset.Name)
return err
}
func uncompressed_nomemcopy(w io.Writer, asset *Asset, r io.Reader) error {
_, err := fmt.Fprintf(w, `var _%s = "`, asset.Func)
if err != nil {
return err
}
_, err = io.Copy(&StringWriter{Writer: w}, r)
if err != nil {
return err
}
_, err = fmt.Fprintf(w, `"
func %s_bytes() ([]byte, error) {
return bindata_read(
_%s,
%q,
)
}
`, asset.Func, asset.Func, asset.Name)
return err
}
func uncompressed_memcopy(w io.Writer, asset *Asset, r io.Reader) error {
_, err := fmt.Fprintf(w, `var _%s = []byte(`, asset.Func)
if err != nil {
return err
}
b, err := ioutil.ReadAll(r)
if err != nil {
return err
}
if utf8.Valid(b) {
fmt.Fprintf(w, "`%s`", sanitize(b))
} else {
fmt.Fprintf(w, "%q", b)
}
_, err = fmt.Fprintf(w, `)
func %s_bytes() ([]byte, error) {
return _%s, nil
}
`, asset.Func, asset.Func)
return err
}
func asset_release_common(w io.Writer, c *Config, asset *Asset) error {
fi, err := os.Stat(asset.Path)
if err != nil {
return err
}
mode := uint(fi.Mode())
modTime := fi.ModTime().Unix()
if c.Mode > 0 {
mode = uint(os.ModePerm) & c.Mode
}
if c.ModTime > 0 {
modTime = c.ModTime
}
_, err = fmt.Fprintf(w, `func %s() (*asset, error) {
bytes, err := %s_bytes()
if err != nil {
return nil, err
}
info := bindata_file_info{name: %q, size: %d, mode: os.FileMode(%d), modTime: time.Unix(%d, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
`, asset.Func, asset.Func, asset.Name, fi.Size(), mode, modTime)
return err
}