-
Notifications
You must be signed in to change notification settings - Fork 18.8k
Description
There are many tools to embed static asset files into binaries:
- https://godoc.org/perkeep.org/pkg/fileembed / perkeep.org/pkg/fileembed/genfileembed
- https://godoc.org/github.com/gobuffalo/packr
- https://godoc.org/github.com/knadh/stuffbin
- https://github.com/rakyll/statik
- Bazel go_embed_data
Actually, https://tech.townsourced.com/post/embedding-static-files-in-go/ lists more:
- vfsgen - https://github.com/shurcooL/vfsgen
- go.rice - https://github.com/GeertJohan/go.rice
- statik - https://github.com/rakyll/statik
- esc - https://github.com/mjibson/esc
- go-embed - https://github.com/pyros2097/go-embed
- go-resources - https://github.com/omeid/go-resources
- statics - https://github.com/go-playground/statics
- templify - https://github.com/wlbr/templify
- gnoso/go-bindata - https://github.com/gnoso/go-bindata
- shuLhan/go-bindata - https://github.com/shuLhan/go-bindata
- fileb0x - https://github.com/UnnoTed/fileb0x
- gobundle - https://github.com/alecthomas/gobundle
- parcello - https://github.com/phogolabs/parcello
Proposal
I think it's time to do this well once & reduce duplication, adding official support for embedding file resources into the cmd/go tool.
Problems with the current situation:
- There are too many tools
- Using a go:generate-based solution bloats the git history with a second (and slightly larger) copy of each file.
- Not using go:generate means not being
go install-able or making people write their own Makefiles, etc.
Goals:
- don't check in generated files
- don't generate *.go files at all (at least not in user's workspace)
- make
go install/go builddo the embedding automatically - let user choose per file/glob which type of access is needed (e.g. []byte,
func() io.Reader,io.ReaderAt, etc) - Maybe store assets compressed in the binary where appropriate (e.g. if user only needs an
io.Reader)? (edit: but probably not; see comments below) - No code execution at compilation time; that is a long-standing Go policy.
go buildorgo installcan not run arbitrary code, just likego:generatedoesn't run automatically at install time.
The two main implementation approaches are //go:embed Logo logo.jpg or a well-known package (var Logo = embed.File("logo.jpg")).
go:embed approach
For a go:embed approach, one might say that any go/build-selected *.go file can contain something like:
//go:embed Logo logo.jpg
Which, say, compiles to:
func Logo() *io.SectionReader(adding a dependency to the io package)
Or:
//go:embedglob Assets assets/*.css assets/*.js
compiling to, say:
var Assets interface{
Files() []string
Open func(name string) *io.SectionReader
} = runtime.EmbedAsset(123)
Obviously this isn't fully fleshed out. There'd need to be something for compressed files too that yield only an io.Reader.
embed package approach
The other high-level approach is to not have a magic //go:embed syntax and instead just let users write Go code in some new "embed" or "golang.org/x/foo/embed" package:
var Static = embed.Dir("static")
var Logo = embed.File("images/logo.jpg")
var Words = embed.CompressedReader("dict/words")
Then have cmd/go recognize the calls to embed.Foo("foo/*.js") etc and glob do the work in cmd/go, rather than at runtime. Or maybe certain build tags or flags could make it fall back to doing things at runtime instead. Perkeep (linked above) has such a mode, which is nice to speed up incremental development where you don't care about linking one big binary.
Concerns
- Pick a style (//go:embed* vs a magic package).
- Block certain files?
- Probably block embedding
../../../../../../../../../../etc/shadow - Maybe block reaching into
.gittoo
- Probably block embedding