main.go 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  1. // Copyright 2018-present the CoreDHCP Authors. All rights reserved
  2. // This source code is licensed under the MIT license found in the
  3. // LICENSE file in the root directory of this source tree.
  4. package main
  5. import (
  6. "bufio"
  7. "fmt"
  8. "html/template"
  9. "log"
  10. "os"
  11. "path"
  12. "sort"
  13. "strings"
  14. flag "github.com/spf13/pflag"
  15. )
  16. const (
  17. defaultTemplateFile = "coredhcp.go.template"
  18. importBase = "github.com/coredhcp/coredhcp/"
  19. )
  20. var (
  21. flagTemplate = flag.StringP("template", "t", defaultTemplateFile, "Template file name")
  22. flagOutfile = flag.StringP("outfile", "o", "", "Output file path")
  23. flagFromFile = flag.StringP("from", "f", "", "Optional file name to get the plugin list from, one import path per line")
  24. )
  25. var funcMap = template.FuncMap{
  26. "importname": func(importPath string) (string, error) {
  27. parts := strings.Split(importPath, "/")
  28. if len(parts) < 1 {
  29. return "", fmt.Errorf("no components found in import path '%s'", importPath)
  30. }
  31. return "pl_" + parts[len(parts)-1], nil
  32. },
  33. }
  34. func usage() {
  35. fmt.Fprintf(flag.CommandLine.Output(),
  36. "%s [-template tpl] [-outfile out] [-from pluginlist] [plugin [plugin...]]\n",
  37. os.Args[0],
  38. )
  39. flag.PrintDefaults()
  40. fmt.Fprintln(flag.CommandLine.Output(), ` plugin
  41. Plugin name to include, as go import path.
  42. Short names can be used for builtin coredhcp plugins (eg "serverid")`)
  43. }
  44. func main() {
  45. flag.Usage = usage
  46. flag.Parse()
  47. data, err := os.ReadFile(*flagTemplate)
  48. if err != nil {
  49. log.Fatalf("Failed to read template file '%s': %v", *flagTemplate, err)
  50. }
  51. t, err := template.New("coredhcp").Funcs(funcMap).Parse(string(data))
  52. if err != nil {
  53. log.Fatalf("Template parsing failed: %v", err)
  54. }
  55. plugins := make(map[string]bool)
  56. for _, pl := range flag.Args() {
  57. pl := strings.TrimSpace(pl)
  58. if pl == "" {
  59. continue
  60. }
  61. if !strings.ContainsRune(pl, '/') {
  62. // A bare name was specified, not a full import path.
  63. // Coredhcp plugins aren't in the standard library, and it's unlikely someone
  64. // would put them at the base of $GOPATH/src.
  65. // Assume this is one of the builtin plugins. If needed, use the -from option
  66. // which always requires (and uses) exact paths
  67. // XXX: we could also look into github.com/coredhcp/plugins
  68. pl = importBase + pl
  69. }
  70. plugins[pl] = true
  71. }
  72. if *flagFromFile != "" {
  73. // additional plugin names from a text file, one line per plugin import
  74. // path
  75. fd, err := os.Open(*flagFromFile)
  76. if err != nil {
  77. log.Fatalf("Failed to read file '%s': %v", *flagFromFile, err)
  78. }
  79. defer func() {
  80. if err := fd.Close(); err != nil {
  81. log.Printf("Error closing file '%s': %v", *flagFromFile, err)
  82. }
  83. }()
  84. sc := bufio.NewScanner(fd)
  85. for sc.Scan() {
  86. pl := strings.TrimSpace(sc.Text())
  87. if pl == "" {
  88. continue
  89. }
  90. plugins[pl] = true
  91. }
  92. if err := sc.Err(); err != nil {
  93. log.Fatalf("Error reading file '%s': %v", *flagFromFile, err)
  94. }
  95. }
  96. if len(plugins) == 0 {
  97. log.Fatalf("No plugin specified!")
  98. }
  99. outfile := *flagOutfile
  100. if outfile == "" {
  101. tmpdir, err := os.MkdirTemp("", "coredhcp")
  102. if err != nil {
  103. log.Fatalf("Cannot create temporary directory: %v", err)
  104. }
  105. outfile = path.Join(tmpdir, "coredhcp.go")
  106. }
  107. log.Printf("Generating output file '%s' with %d plugin(s):", outfile, len(plugins))
  108. idx := 1
  109. for pl := range plugins {
  110. log.Printf("% 3d) %s", idx, pl)
  111. idx++
  112. }
  113. outFD, err := os.OpenFile(outfile, os.O_CREATE|os.O_WRONLY, 0644)
  114. if err != nil {
  115. log.Fatalf("Failed to create output file '%s': %v", outfile, err)
  116. }
  117. defer func() {
  118. if err := outFD.Close(); err != nil {
  119. log.Printf("Error while closing file descriptor for '%s': %v", outfile, err)
  120. }
  121. }()
  122. // WARNING: no escaping of the provided strings is done
  123. pluginList := make([]string, 0, len(plugins))
  124. for pl := range plugins {
  125. pluginList = append(pluginList, pl)
  126. }
  127. sort.Strings(pluginList)
  128. if err := t.Execute(outFD, pluginList); err != nil {
  129. log.Fatalf("Template execution failed: %v", err)
  130. }
  131. log.Printf("Generated file '%s'. You can build it by running 'go build' in the output directory.", outfile)
  132. fmt.Println(path.Dir(outfile))
  133. }