blob: 2aa1477deee9dd9d59d27e1eaf12060aaaf3b153 [file] [log] [blame]
avm99963cb0d0122020-12-26 20:01:18 +01001// Utility to generate web extension manifest files from GJSON (Generic JSON)
2// templates, given some dependencies.
3package main
4
5import (
6 "bufio"
7 "flag"
8 "fmt"
9 "io"
10 "log"
11 "os"
12 "regexp"
13 "strings"
14)
15
16var beginningOfIfStatement = regexp.MustCompile(`^\s*#if defined\(([^\(\)]*)\)\s*$`)
17var endOfIfStatement = regexp.MustCompile(`^\s*#endif\s*$`)
18
19var (
avm999637cb67c52021-02-18 19:16:53 +010020 quietMode = flag.Bool("quiet", false, "Quiet mode")
avm99963cb0d0122020-12-26 20:01:18 +010021 templateFile = flag.String("template", "", "Template file")
avm999637cb67c52021-02-18 19:16:53 +010022 destFile = flag.String("dest", "", "Destination file")
avm99963cb0d0122020-12-26 20:01:18 +010023)
24
25// findWithCaseFolding returns whether there is some element of slice which is
26// equal to val under Unicode case-folding.
27func findWithCaseFolding(slice []string, val string) bool {
28 for _, item := range slice {
29 if strings.EqualFold(item, val) {
30 return true
31 }
32 }
33 return false
34}
35
36// Given a Generic JSON (GJSON) template and the dependencies met,
37// WriteManifest will write at dest the manifest file generated from the
38// template according to the dependencies.
39func WriteManifest(template io.Reader, dest io.Writer, dependencies []string) error {
40 level := 0
41 activeLevel := 0
42 scanner := bufio.NewScanner(template)
43 for scanner.Scan() {
44 line := scanner.Text()
45 considerLine := false
46 if m := beginningOfIfStatement.FindStringSubmatch(line); m != nil {
47 if level == activeLevel {
48 statementDeps := m[1]
49 deps := strings.Split(statementDeps, "||")
50 for _, dep := range deps {
51 dep = strings.TrimSpace(dep)
52 if findWithCaseFolding(dependencies, dep) {
53 activeLevel++
54 break
55 }
56 }
57 }
58 level++
59 } else if m := endOfIfStatement.MatchString(line); m {
60 if activeLevel == level {
61 activeLevel--
62 }
63 level--
64 } else {
65 considerLine = level == activeLevel
66 }
67
68 if considerLine {
69 _, err := io.WriteString(dest, line+"\n")
70 if err != nil {
71 return fmt.Errorf("Can't write manifest: %v", err)
72 }
73 }
74 }
75
76 return nil
77}
78
79// WriteManifestFileNames is the same as WriteManifest, but instead of receiving
80// read and write handlers for each file, this function receives the file names
81// as strings and reads/writes from/to those files.
82func WriteManifestFileNames(templateFile string, destFile string, dependencies []string) error {
83 template, err := os.Open(templateFile)
84 if err != nil {
85 return fmt.Errorf("Couldn't open file %v: %v", templateFile, err)
86 }
87 defer template.Close()
88
89 dest, err := os.Create(destFile)
90 if err != nil {
91 return fmt.Errorf("Couldn't create file %v: %v", destFile, err)
92 }
93 defer dest.Close()
94
95 return WriteManifest(template, dest, dependencies)
96}
97
98func main() {
99 log.SetPrefix("generatemanifest: ")
avm999637cb67c52021-02-18 19:16:53 +0100100 log.SetFlags(0)
avm99963cb0d0122020-12-26 20:01:18 +0100101
avm999637cb67c52021-02-18 19:16:53 +0100102 flag.Parse()
103 dependencies := flag.Args()
avm99963cb0d0122020-12-26 20:01:18 +0100104
avm999637cb67c52021-02-18 19:16:53 +0100105 if len(dependencies) == 0 {
106 log.Fatalf("Pass the dependencies as arguments (for instance, run `go run generateManifest.go CHROMIUM`).")
107 }
108 if *templateFile == "" {
avm99963cb0d0122020-12-26 20:01:18 +0100109 log.Fatalf("Pass the template file name via the -template flag.")
110 }
avm999637cb67c52021-02-18 19:16:53 +0100111 if *destFile == "" {
avm99963cb0d0122020-12-26 20:01:18 +0100112 log.Fatalf("Pass the destination file name via the -dest flag.")
113 }
114
115 err := WriteManifestFileNames(*templateFile, *destFile, dependencies)
avm999637cb67c52021-02-18 19:16:53 +0100116 if err != nil {
117 log.Fatalf("%v", err)
118 } else if !*quietMode {
119 log.Println("Manifest has been generated successfully")
120 }
avm99963cb0d0122020-12-26 20:01:18 +0100121}