• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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