genassets.go 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148
  1. // Copyright (C) 2014 The Syncthing Authors.
  2. //
  3. // This Source Code Form is subject to the terms of the Mozilla Public
  4. // License, v. 2.0. If a copy of the MPL was not distributed with this file,
  5. // You can obtain one at https://mozilla.org/MPL/2.0/.
  6. // +build ignore
  7. package main
  8. import (
  9. "bytes"
  10. "compress/gzip"
  11. "flag"
  12. "fmt"
  13. "go/format"
  14. "io"
  15. "io/ioutil"
  16. "os"
  17. "path/filepath"
  18. "strconv"
  19. "strings"
  20. "text/template"
  21. "time"
  22. )
  23. var tpl = template.Must(template.New("assets").Parse(`// Code generated by genassets.go - DO NOT EDIT.
  24. package auto
  25. import (
  26. "time"
  27. "github.com/syncthing/syncthing/lib/assets"
  28. )
  29. func Assets() map[string]assets.Asset {
  30. var ret = make(map[string]assets.Asset, {{.Assets | len}})
  31. t := time.Unix({{.Generated}}, 0)
  32. {{range $asset := .Assets}}
  33. ret["{{$asset.Name}}"] = assets.Asset{
  34. Content: {{$asset.Data}},
  35. Gzipped: {{$asset.Gzipped}},
  36. Length: {{$asset.Length}},
  37. Filename: {{$asset.Name | printf "%q"}},
  38. Modified: t,
  39. }
  40. {{end}}
  41. return ret
  42. }
  43. `))
  44. type asset struct {
  45. Name string
  46. Data string
  47. Length int
  48. Gzipped bool
  49. }
  50. var assets []asset
  51. func walkerFor(basePath string) filepath.WalkFunc {
  52. return func(name string, info os.FileInfo, err error) error {
  53. if err != nil {
  54. return err
  55. }
  56. if strings.HasPrefix(filepath.Base(name), ".") {
  57. // Skip dotfiles
  58. return nil
  59. }
  60. if info.Mode().IsRegular() {
  61. data, err := ioutil.ReadFile(name)
  62. if err != nil {
  63. return err
  64. }
  65. length := len(data)
  66. var buf bytes.Buffer
  67. gw, _ := gzip.NewWriterLevel(&buf, gzip.BestCompression)
  68. gw.Write(data)
  69. gw.Close()
  70. // Only replace asset by gzipped version if it is smaller.
  71. // In practice, this means HTML, CSS, SVG etc. get compressed,
  72. // while PNG and WOFF files are left uncompressed.
  73. // lib/assets detects gzip and sets headers/decompresses.
  74. gzipped := buf.Len() < len(data)
  75. if gzipped {
  76. data = buf.Bytes()
  77. }
  78. name, _ = filepath.Rel(basePath, name)
  79. assets = append(assets, asset{
  80. Name: filepath.ToSlash(name),
  81. Data: fmt.Sprintf("%q", string(data)),
  82. Length: length,
  83. Gzipped: gzipped,
  84. })
  85. }
  86. return nil
  87. }
  88. }
  89. type templateVars struct {
  90. Assets []asset
  91. Generated int64
  92. }
  93. func main() {
  94. outfile := flag.String("o", "", "Name of output file (default stdout)")
  95. flag.Parse()
  96. filepath.Walk(flag.Arg(0), walkerFor(flag.Arg(0)))
  97. var buf bytes.Buffer
  98. // Generated time is now, except if the SOURCE_DATE_EPOCH environment
  99. // variable is set (for reproducible builds).
  100. generated := time.Now().Unix()
  101. if s, _ := strconv.ParseInt(os.Getenv("SOURCE_DATE_EPOCH"), 10, 64); s > 0 {
  102. generated = s
  103. }
  104. tpl.Execute(&buf, templateVars{
  105. Assets: assets,
  106. Generated: generated,
  107. })
  108. bs, err := format.Source(buf.Bytes())
  109. if err != nil {
  110. fmt.Fprintln(os.Stderr, err)
  111. os.Exit(1)
  112. }
  113. out := io.Writer(os.Stdout)
  114. if *outfile != "" {
  115. out, err = os.Create(*outfile)
  116. if err != nil {
  117. fmt.Fprintln(os.Stderr, err)
  118. os.Exit(1)
  119. }
  120. }
  121. out.Write(bs)
  122. }