1// import_tool is a quick tool for importing Chromium's certificate verifier 2// code into google3. In time it might be replaced by Copybara, but this is a 3// lighter-weight solution while we're quickly iterating and only going in one 4// direction. 5// 6// Usage: ./import_tool -spec import_spec.json\ 7// -source-base ~/src/chromium/src/net\ 8// -dest-base . 9package main 10 11import ( 12 "bufio" 13 "encoding/json" 14 "errors" 15 "flag" 16 "fmt" 17 "io/ioutil" 18 "os" 19 "path/filepath" 20 "regexp" 21 "strings" 22 "sync" 23 "sync/atomic" 24) 25 26type specification struct { 27 Replacements []replacement `json:"replacements"` 28 Files []string `json:"files"` 29} 30 31type replacement struct { 32 Match string `json:"match"` 33 matchRE *regexp.Regexp `json:"-"` 34 Replace string `json:"replace"` 35 Include string `json:"include"` 36 Using []string `json:"using"` 37 used uint32 38} 39 40var ( 41 specFile *string = flag.String("spec", "", "Location of spec JSON") 42 sourceBase *string = flag.String("source-base", "", "Path of the source files") 43 destBase *string = flag.String("dest-base", "", "Path of the destination files") 44) 45 46func transformFile(spec *specification, filename string) error { 47 const newLine = "\n" 48 49 sourcePath := filepath.Join(*sourceBase, filename) 50 destPath := filename 51 destPath = strings.TrimPrefix(destPath, "net/") 52 destPath = strings.TrimPrefix(destPath, "cert/") 53 destPath = strings.TrimPrefix(destPath, "der/") 54 destPath = strings.TrimPrefix(destPath, "pki/") 55 destPath = filepath.Join(*destBase, destPath) 56 destDir := filepath.Dir(destPath) 57 if err := os.MkdirAll(destDir, 0755); err != nil { 58 return err 59 } 60 61 source, err := os.Open(sourcePath) 62 if err != nil { 63 return err 64 } 65 defer source.Close() 66 67 dest, err := os.OpenFile(destPath, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0644) 68 if err != nil { 69 return err 70 } 71 defer dest.Close() 72 73 var using []string 74 var includeInsertionPoint int 75 includes := make(map[string]struct{}) 76 scanner := bufio.NewScanner(source) 77 out := "" 78 for scanner.Scan() { 79 line := scanner.Text() 80 81 if includeInsertionPoint == 0 && len(line) > 0 && 82 !strings.HasPrefix(line, "// ") && 83 !strings.HasPrefix(line, "#if") && 84 !strings.HasPrefix(line, "#define ") { 85 includeInsertionPoint = len(out) 86 } 87 88 for i, repl := range spec.Replacements { 89 if !repl.matchRE.MatchString(line) { 90 continue 91 } 92 line = repl.matchRE.ReplaceAllString(line, repl.Replace) 93 atomic.StoreUint32(&spec.Replacements[i].used, 1) 94 using = append(using, repl.Using...) 95 if repl.Include != "" { 96 includes[repl.Include] = struct{}{} 97 } 98 } 99 100 for _, u := range using { 101 line = strings.Replace( 102 line, "namespace chromium_certificate_verifier {", 103 "namespace chromium_certificate_verifier {\nusing "+u+";", 1) 104 } 105 106 out += line 107 out += newLine 108 } 109 110 if len(includes) > 0 { 111 if includeInsertionPoint == 0 { 112 panic("failed to find include insertion point for " + filename) 113 } 114 115 var s string 116 for include := range includes { 117 s = s + "#include \"" + include + "\"\n" 118 } 119 120 out = out[:includeInsertionPoint] + s + out[includeInsertionPoint:] 121 } 122 123 dest.WriteString(out) 124 fmt.Printf("%s\n", filename) 125 126 return nil 127} 128 129func do() error { 130 flag.Parse() 131 132 specBytes, err := ioutil.ReadFile(*specFile) 133 if err != nil { 134 return err 135 } 136 137 var spec specification 138 if err := json.Unmarshal(specBytes, &spec); err != nil { 139 if jsonError, ok := err.(*json.SyntaxError); ok { 140 return fmt.Errorf("JSON parse error at offset %v: %v", jsonError.Offset, err.Error()) 141 } 142 return errors.New("JSON parse error: " + err.Error()) 143 } 144 145 for i, repl := range spec.Replacements { 146 var err error 147 spec.Replacements[i].matchRE, err = regexp.Compile(repl.Match) 148 if err != nil { 149 return fmt.Errorf("Failed to parse %q: %s", repl.Match, err) 150 } 151 } 152 153 errors := make(chan error, len(spec.Files)) 154 var wg sync.WaitGroup 155 156 for _, filename := range spec.Files { 157 wg.Add(1) 158 159 go func(filename string) { 160 if err := transformFile(&spec, filename); err != nil { 161 errors <- err 162 } 163 wg.Done() 164 }(filename) 165 } 166 167 wg.Wait() 168 select { 169 case err := <-errors: 170 return err 171 default: 172 break 173 } 174 for _, repl := range spec.Replacements { 175 if repl.used == 0 { 176 fmt.Fprintf(os.Stderr, "replacement for \"%s\" not used\n", repl.Match) 177 } 178 } 179 return nil 180} 181 182func main() { 183 if err := do(); err != nil { 184 fmt.Fprintf(os.Stderr, "%s\n", err) 185 os.Exit(1) 186 } 187} 188