| | |
| | |
| | |
| |
|
| | package staticdata |
| |
|
| | import ( |
| | "path" |
| | "sort" |
| | "strings" |
| |
|
| | "cmd/compile/internal/base" |
| | "cmd/compile/internal/ir" |
| | "cmd/compile/internal/objw" |
| | "cmd/compile/internal/types" |
| | "cmd/internal/obj" |
| | ) |
| |
|
| | const ( |
| | embedUnknown = iota |
| | embedBytes |
| | embedString |
| | embedFiles |
| | ) |
| |
|
| | func embedFileList(v *ir.Name, kind int) []string { |
| | |
| | have := make(map[string]bool) |
| | var list []string |
| | for _, e := range *v.Embed { |
| | for _, pattern := range e.Patterns { |
| | files, ok := base.Flag.Cfg.Embed.Patterns[pattern] |
| | if !ok { |
| | base.ErrorfAt(e.Pos, 0, "invalid go:embed: build system did not map pattern: %s", pattern) |
| | } |
| | for _, file := range files { |
| | if base.Flag.Cfg.Embed.Files[file] == "" { |
| | base.ErrorfAt(e.Pos, 0, "invalid go:embed: build system did not map file: %s", file) |
| | continue |
| | } |
| | if !have[file] { |
| | have[file] = true |
| | list = append(list, file) |
| | } |
| | if kind == embedFiles { |
| | for dir := path.Dir(file); dir != "." && !have[dir]; dir = path.Dir(dir) { |
| | have[dir] = true |
| | list = append(list, dir+"/") |
| | } |
| | } |
| | } |
| | } |
| | } |
| | sort.Slice(list, func(i, j int) bool { |
| | return embedFileLess(list[i], list[j]) |
| | }) |
| |
|
| | if kind == embedString || kind == embedBytes { |
| | if len(list) > 1 { |
| | base.ErrorfAt(v.Pos(), 0, "invalid go:embed: multiple files for type %v", v.Type()) |
| | return nil |
| | } |
| | } |
| |
|
| | return list |
| | } |
| |
|
| | |
| | func embedKind(typ *types.Type) int { |
| | if typ.Sym() != nil && typ.Sym().Name == "FS" && typ.Sym().Pkg.Path == "embed" { |
| | return embedFiles |
| | } |
| | if typ.Kind() == types.TSTRING { |
| | return embedString |
| | } |
| | if typ.IsSlice() && typ.Elem().Kind() == types.TUINT8 { |
| | return embedBytes |
| | } |
| | return embedUnknown |
| | } |
| |
|
| | func embedFileNameSplit(name string) (dir, elem string, isDir bool) { |
| | name, isDir = strings.CutSuffix(name, "/") |
| | i := strings.LastIndexByte(name, '/') |
| | if i < 0 { |
| | return ".", name, isDir |
| | } |
| | return name[:i], name[i+1:], isDir |
| | } |
| |
|
| | |
| | |
| | func embedFileLess(x, y string) bool { |
| | xdir, xelem, _ := embedFileNameSplit(x) |
| | ydir, yelem, _ := embedFileNameSplit(y) |
| | return xdir < ydir || xdir == ydir && xelem < yelem |
| | } |
| |
|
| | |
| | |
| | func WriteEmbed(v *ir.Name) { |
| | |
| |
|
| | commentPos := (*v.Embed)[0].Pos |
| | if base.Flag.Cfg.Embed.Patterns == nil { |
| | base.ErrorfAt(commentPos, 0, "invalid go:embed: build system did not supply embed configuration") |
| | return |
| | } |
| | kind := embedKind(v.Type()) |
| | if kind == embedUnknown { |
| | base.ErrorfAt(v.Pos(), 0, "go:embed cannot apply to var of type %v", v.Type()) |
| | return |
| | } |
| |
|
| | files := embedFileList(v, kind) |
| | if base.Errors() > 0 { |
| | return |
| | } |
| | switch kind { |
| | case embedString, embedBytes: |
| | file := files[0] |
| | fsym, size, err := fileStringSym(v.Pos(), base.Flag.Cfg.Embed.Files[file], kind == embedString, nil) |
| | if err != nil { |
| | base.ErrorfAt(v.Pos(), 0, "embed %s: %v", file, err) |
| | } |
| | sym := v.Linksym() |
| | off := 0 |
| | off = objw.SymPtr(sym, off, fsym, 0) |
| | off = objw.Uintptr(sym, off, uint64(size)) |
| | if kind == embedBytes { |
| | objw.Uintptr(sym, off, uint64(size)) |
| | } |
| |
|
| | case embedFiles: |
| | slicedata := v.Sym().Pkg.Lookup(v.Sym().Name + `.files`).Linksym() |
| | off := 0 |
| | |
| | off = objw.SymPtr(slicedata, off, slicedata, 3*types.PtrSize) |
| | off = objw.Uintptr(slicedata, off, uint64(len(files))) |
| | off = objw.Uintptr(slicedata, off, uint64(len(files))) |
| |
|
| | |
| | |
| | |
| | |
| | |
| | const hashSize = 16 |
| | hash := make([]byte, hashSize) |
| | for _, file := range files { |
| | off = objw.SymPtr(slicedata, off, StringSym(v.Pos(), file), 0) |
| | off = objw.Uintptr(slicedata, off, uint64(len(file))) |
| | if strings.HasSuffix(file, "/") { |
| | |
| | off = objw.Uintptr(slicedata, off, 0) |
| | off = objw.Uintptr(slicedata, off, 0) |
| | off += hashSize |
| | } else { |
| | fsym, size, err := fileStringSym(v.Pos(), base.Flag.Cfg.Embed.Files[file], true, hash) |
| | if err != nil { |
| | base.ErrorfAt(v.Pos(), 0, "embed %s: %v", file, err) |
| | } |
| | off = objw.SymPtr(slicedata, off, fsym, 0) |
| | off = objw.Uintptr(slicedata, off, uint64(size)) |
| | off = int(slicedata.WriteBytes(base.Ctxt, int64(off), hash)) |
| | } |
| | } |
| | objw.Global(slicedata, int32(off), obj.RODATA|obj.LOCAL) |
| | sym := v.Linksym() |
| | objw.SymPtr(sym, 0, slicedata, 0) |
| | } |
| | } |
| |
|