package main import ( "archive/zip" "bufio" "fmt" "io" "io/fs" "log" "os" "path/filepath" ) const templatesPath = "../../templates" // This program generates zz_filesystem_generated.go file containing byte array variable named TemplatesZip. // The variable contains zip of "./templates" directory. func main() { f, err := os.OpenFile("../../generate/zz_filesystem_generated.go", os.O_TRUNC|os.O_CREATE|os.O_WRONLY, 0644) if err != nil { log.Fatal(err) } defer f.Close() srcOut := bufio.NewWriter(f) defer srcOut.Flush() _, err = fmt.Fprintf(srcOut, "// Code generated by go generate; DO NOT EDIT.\npackage generate\n\nvar TemplatesZip = []byte{") if err != nil { log.Fatal(err) } zipWriter := zip.NewWriter(newGoByteArrayWriter(srcOut)) buff := make([]byte, 4*1024) err = filepath.Walk(templatesPath, func(path string, info fs.FileInfo, err error) error { if err != nil { return err } name, err := filepath.Rel(templatesPath, path) if err != nil { return err } if name == "." { return nil } name = filepath.ToSlash(name) if info.IsDir() { name = name + "/" } header := &zip.FileHeader{ Name: name, Method: zip.Deflate, } // Coercing permission to 755 for directories/executables and to 644 for non-executable files. // This is needed to ensure reproducible builds on machines with different values of `umask`. var mode fs.FileMode switch { case info.Mode()&fs.ModeSymlink != 0: mode = 0777 | fs.ModeSymlink case info.IsDir() || (info.Mode().Perm()&0111) != 0: // dir or executable mode = 0755 case info.Mode()&fs.ModeType == 0: // regular file mode = 0644 default: return fmt.Errorf("unsupported file type: %s", info.Mode().String()) } header.SetMode(mode) w, err := zipWriter.CreateHeader(header) if err != nil { return err } switch { case info.Mode()&fs.ModeSymlink != 0: symlinkTarget, err := os.Readlink(path) if err != nil { return err } _, err = w.Write([]byte(filepath.ToSlash(symlinkTarget))) return err case info.Mode()&fs.ModeType == 0: // regular file f, err := os.Open(path) if err != nil { return err } defer f.Close() _, err = io.CopyBuffer(w, f, buff) return err default: return nil } }) zipWriter.Close() if err != nil { log.Fatal(err) } _, err = fmt.Fprint(srcOut, "\n}\n") if err != nil { log.Fatal(err) } } // goByteArrayWriter dumps bytes as a Go integer hex literals separated by commas into underlying Writer. // Each line of the output will be indented by a tab and each line will contain at most 32 integer literals. // This is useful when generating Go array literals. type goByteArrayWriter struct { i uint32 w io.Writer hexDigitWithComma []byte } func newGoByteArrayWriter(w io.Writer) *goByteArrayWriter { return &goByteArrayWriter{ i: 0, w: w, hexDigitWithComma: []byte("0x00,"), } } var hexs = []byte("0123456789abcdef") var space = []byte(" ") var newLineAndTab = []byte("\n\t") const bytesInLine = 32 func (g *goByteArrayWriter) Write(bs []byte) (written int, err error) { for _, b := range bs { if g.i == 0 { _, err = g.w.Write(newLineAndTab) if err != nil { return } } else { _, err = g.w.Write(space) if err != nil { return } } g.hexDigitWithComma[2] = hexs[b>>4] g.hexDigitWithComma[3] = hexs[b&0x0f] _, err = g.w.Write(g.hexDigitWithComma) if err != nil { return } if g.i == bytesInLine-1 { g.i = 0 } else { g.i++ } written += 1 } return }