package main import ( "bytes" "encoding/gob" "fmt" "go/build" "io" "io/ioutil" "os" "path/filepath" "regexp" "strings" "text/template" "github.com/GeertJohan/go.rice/embedded" "github.com/akavel/rsrc/coff" ) type sizedReader struct { *bytes.Reader } func (s sizedReader) Size() int64 { return int64(s.Len()) } var tmplEmbeddedSysoHelper *template.Template func init() { var err error tmplEmbeddedSysoHelper, err = template.New("embeddedSysoHelper").Parse(`package {{.Package}} // ############# GENERATED CODE ##################### // ## This file was generated by the rice tool. // ## Do not edit unless you know what you're doing. // ################################################## // extern char _bricebox_{{.Symname}}[], _ericebox_{{.Symname}}; // int get_{{.Symname}}_length() { // return &_ericebox_{{.Symname}} - _bricebox_{{.Symname}}; // } import "C" import ( "bytes" "encoding/gob" "github.com/GeertJohan/go.rice/embedded" "unsafe" ) func init() { ptr := unsafe.Pointer(&C._bricebox_{{.Symname}}) bts := C.GoBytes(ptr, C.get_{{.Symname}}_length()) embeddedBox := &embedded.EmbeddedBox{} err := gob.NewDecoder(bytes.NewReader(bts)).Decode(embeddedBox) if err != nil { panic("error decoding embedded box: "+err.Error()) } embeddedBox.Link() embedded.RegisterEmbeddedBox(embeddedBox.Name, embeddedBox) }`) if err != nil { panic("could not parse template embeddedSysoHelper: " + err.Error()) } } type embeddedSysoHelperData struct { Package string Symname string } func operationEmbedSyso(pkg *build.Package) { regexpSynameReplacer := regexp.MustCompile(`[^a-z0-9_]`) boxMap := findBoxes(pkg) // notify user when no calls to rice.FindBox are made (is this an error and therefore os.Exit(1) ? if len(boxMap) == 0 { fmt.Println("no calls to rice.FindBox() found") return } verbosef("\n") for boxname := range boxMap { // find path and filename for this box boxPath := filepath.Join(pkg.Dir, boxname) boxFilename := strings.Replace(boxname, "/", "-", -1) boxFilename = strings.Replace(boxFilename, "..", "back", -1) boxFilename = strings.Replace(boxFilename, ".", "-", -1) // verbose info verbosef("embedding box '%s'\n", boxname) verbosef("\tto file %s\n", boxFilename) // read box metadata boxInfo, ierr := os.Stat(boxPath) if ierr != nil { fmt.Printf("Error: unable to access box at %s\n", boxPath) os.Exit(1) } // create box datastructure (used by template) box := &embedded.EmbeddedBox{ Name: boxname, Time: boxInfo.ModTime(), EmbedType: embedded.EmbedTypeSyso, Files: make(map[string]*embedded.EmbeddedFile), Dirs: make(map[string]*embedded.EmbeddedDir), } // fill box datastructure with file data filepath.Walk(boxPath, func(path string, info os.FileInfo, err error) error { if err != nil { fmt.Printf("error walking box: %s\n", err) os.Exit(1) } filename := strings.TrimPrefix(path, boxPath) filename = strings.Replace(filename, "\\", "/", -1) filename = strings.TrimPrefix(filename, "/") if info.IsDir() { embeddedDir := &embedded.EmbeddedDir{ Filename: filename, DirModTime: info.ModTime(), } verbosef("\tincludes dir: '%s'\n", embeddedDir.Filename) box.Dirs[embeddedDir.Filename] = embeddedDir // add tree entry (skip for root, it'll create a recursion) if embeddedDir.Filename != "" { pathParts := strings.Split(embeddedDir.Filename, "/") parentDir := box.Dirs[strings.Join(pathParts[:len(pathParts)-1], "/")] parentDir.ChildDirs = append(parentDir.ChildDirs, embeddedDir) } } else { embeddedFile := &embedded.EmbeddedFile{ Filename: filename, FileModTime: info.ModTime(), Content: "", } verbosef("\tincludes file: '%s'\n", embeddedFile.Filename) contentBytes, err := ioutil.ReadFile(path) if err != nil { fmt.Printf("error reading file content while walking box: %s\n", err) os.Exit(1) } embeddedFile.Content = string(contentBytes) box.Files[embeddedFile.Filename] = embeddedFile } return nil }) // encode embedded box to gob file boxGobBuf := &bytes.Buffer{} err := gob.NewEncoder(boxGobBuf).Encode(box) if err != nil { fmt.Printf("error encoding box to gob: %v\n", err) os.Exit(1) } verbosef("gob-encoded embeddedBox is %d bytes large\n", boxGobBuf.Len()) // write coff symname := regexpSynameReplacer.ReplaceAllString(boxname, "_") createCoffSyso(boxname, symname, "386", boxGobBuf.Bytes()) createCoffSyso(boxname, symname, "amd64", boxGobBuf.Bytes()) // write go sysoHelperData := embeddedSysoHelperData{ Package: pkg.Name, Symname: symname, } fileSysoHelper, err := os.Create(boxFilename + ".rice-box.go") if err != nil { fmt.Printf("error creating syso helper: %v\n", err) os.Exit(1) } err = tmplEmbeddedSysoHelper.Execute(fileSysoHelper, sysoHelperData) if err != nil { fmt.Printf("error executing tmplEmbeddedSysoHelper: %v\n", err) os.Exit(1) } } } func createCoffSyso(boxFilename string, symname string, arch string, data []byte) { boxCoff := coff.NewRDATA() switch arch { case "386": case "amd64": boxCoff.FileHeader.Machine = 0x8664 default: panic("invalid arch") } boxCoff.AddData("_bricebox_"+symname, sizedReader{bytes.NewReader(data)}) boxCoff.AddData("_ericebox_"+symname, io.NewSectionReader(strings.NewReader("\000\000"), 0, 2)) // TODO: why? copied from rsrc, which copied it from as-generated boxCoff.Freeze() err := writeCoff(boxCoff, boxFilename+"_"+arch+".rice-box.syso") if err != nil { fmt.Printf("error writing %s coff/.syso: %v\n", arch, err) os.Exit(1) } }