forked from Debian/debiman
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathgoembed.go
119 lines (105 loc) · 2.63 KB
/
goembed.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
// copied from https://github.com/dsymonds/goembed/ with pull requests applied
//go:build ignore
// +build ignore
// goembed generates a Go source file from an input file.
package main
import (
"bytes"
"compress/gzip"
"flag"
"fmt"
"io"
"io/ioutil"
"log"
"os"
"text/template"
)
var (
packageFlag = flag.String("package", "", "Go package name")
varFlag = flag.String("var", "", "Go var name")
gzipFlag = flag.Bool("gzip", false, "Whether to gzip contents")
)
func main() {
flag.Parse()
fmt.Printf("package %s\n\n", *packageFlag)
if flag.NArg() > 0 {
fmt.Println("// Table of contents")
fmt.Printf("var %v = map[string]string{\n", *varFlag)
for i, filename := range flag.Args() {
fmt.Printf("\t%#v: %s_%d,\n", filename, *varFlag, i)
}
fmt.Println("}")
// Using a separate variable for each []byte, instead of
// combining them into a single map literal, enables a storage
// optimization: the compiler places the data directly in the
// program's noptrdata section instead of the heap.
for i, filename := range flag.Args() {
if err := oneVar(fmt.Sprintf("%s_%d", *varFlag, i), filename); err != nil {
log.Fatal(err)
}
}
} else {
if err := oneVarReader(*varFlag, os.Stdin); err != nil {
log.Fatal(err)
}
}
}
func oneVar(varName, filename string) error {
f, err := os.Open(filename)
if err != nil {
return err
}
defer f.Close()
return oneVarReader(varName, f)
}
func oneVarReader(varName string, r io.Reader) error {
raw, err := ioutil.ReadAll(r)
if err != nil {
return err
}
// Generate []byte(<big string constant>) instead of []byte{<list of byte values>}.
// The latter causes a memory explosion in the compiler (60 MB of input chews over 9 GB RAM).
// Doing a string conversion avoids some of that, but incurs a slight startup cost.
if !*gzipFlag {
fmt.Printf(`var %s = "`, varName)
} else {
var buf bytes.Buffer
gzw, _ := gzip.NewWriterLevel(&buf, gzip.BestCompression)
if _, err := gzw.Write(raw); err != nil {
return err
}
if err := gzw.Close(); err != nil {
return err
}
gz := buf.Bytes()
if err := gzipPrologue.Execute(os.Stdout, varName); err != nil {
return err
}
fmt.Printf("var %s string // set in init\n\n", varName)
fmt.Printf(`var %s_gzip = "`, varName)
raw = gz
}
for _, b := range raw {
fmt.Printf("\\x%02x", b)
}
fmt.Println(`"`)
return nil
}
var gzipPrologue = template.Must(template.New("").Parse(`
import (
"bytes"
"compress/gzip"
"io/ioutil"
)
func init() {
r, err := gzip.NewReader(bytes.NewReader({{.}}_gzip))
if err != nil {
panic(err)
}
defer r.Close()
{{.}}, err = ioutil.ReadAll(r)
if err != nil {
panic(err)
}
}
`))