xref: /aosp_15_r20/external/boringssl/src/util/pregenerate/pregenerate.go (revision 8fb009dc861624b67b6cdb62ea21f0f22d0c584b)
1*8fb009dcSAndroid Build Coastguard Worker// Copyright (c) 2024, Google Inc.
2*8fb009dcSAndroid Build Coastguard Worker//
3*8fb009dcSAndroid Build Coastguard Worker// Permission to use, copy, modify, and/or distribute this software for any
4*8fb009dcSAndroid Build Coastguard Worker// purpose with or without fee is hereby granted, provided that the above
5*8fb009dcSAndroid Build Coastguard Worker// copyright notice and this permission notice appear in all copies.
6*8fb009dcSAndroid Build Coastguard Worker//
7*8fb009dcSAndroid Build Coastguard Worker// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
8*8fb009dcSAndroid Build Coastguard Worker// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9*8fb009dcSAndroid Build Coastguard Worker// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
10*8fb009dcSAndroid Build Coastguard Worker// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11*8fb009dcSAndroid Build Coastguard Worker// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
12*8fb009dcSAndroid Build Coastguard Worker// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
13*8fb009dcSAndroid Build Coastguard Worker// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
14*8fb009dcSAndroid Build Coastguard Worker
15*8fb009dcSAndroid Build Coastguard Worker// pregenerate manages generated files in BoringSSL
16*8fb009dcSAndroid Build Coastguard Workerpackage main
17*8fb009dcSAndroid Build Coastguard Worker
18*8fb009dcSAndroid Build Coastguard Workerimport (
19*8fb009dcSAndroid Build Coastguard Worker	"bytes"
20*8fb009dcSAndroid Build Coastguard Worker	"encoding/json"
21*8fb009dcSAndroid Build Coastguard Worker	"errors"
22*8fb009dcSAndroid Build Coastguard Worker	"flag"
23*8fb009dcSAndroid Build Coastguard Worker	"fmt"
24*8fb009dcSAndroid Build Coastguard Worker	"os"
25*8fb009dcSAndroid Build Coastguard Worker	"path/filepath"
26*8fb009dcSAndroid Build Coastguard Worker	"runtime"
27*8fb009dcSAndroid Build Coastguard Worker	"slices"
28*8fb009dcSAndroid Build Coastguard Worker	"strings"
29*8fb009dcSAndroid Build Coastguard Worker	"sync"
30*8fb009dcSAndroid Build Coastguard Worker
31*8fb009dcSAndroid Build Coastguard Worker	"boringssl.googlesource.com/boringssl/util/build"
32*8fb009dcSAndroid Build Coastguard Worker)
33*8fb009dcSAndroid Build Coastguard Worker
34*8fb009dcSAndroid Build Coastguard Workervar (
35*8fb009dcSAndroid Build Coastguard Worker	check      = flag.Bool("check", false, "Check whether any files need to be updated, without actually updating them")
36*8fb009dcSAndroid Build Coastguard Worker	numWorkers = flag.Int("num-workers", runtime.NumCPU(), "Runs the given number of workers")
37*8fb009dcSAndroid Build Coastguard Worker	dryRun     = flag.Bool("dry-run", false, "Skip actually writing any files")
38*8fb009dcSAndroid Build Coastguard Worker	perlPath   = flag.String("perl", "perl", "Path to the perl command")
39*8fb009dcSAndroid Build Coastguard Worker	list       = flag.Bool("list", false, "List all generated files, rather than actually run them")
40*8fb009dcSAndroid Build Coastguard Worker)
41*8fb009dcSAndroid Build Coastguard Worker
42*8fb009dcSAndroid Build Coastguard Workerfunc runTask(t Task) error {
43*8fb009dcSAndroid Build Coastguard Worker	expected, err := t.Run()
44*8fb009dcSAndroid Build Coastguard Worker	if err != nil {
45*8fb009dcSAndroid Build Coastguard Worker		return err
46*8fb009dcSAndroid Build Coastguard Worker	}
47*8fb009dcSAndroid Build Coastguard Worker
48*8fb009dcSAndroid Build Coastguard Worker	dst := t.Destination()
49*8fb009dcSAndroid Build Coastguard Worker	dstPath := filepath.FromSlash(dst)
50*8fb009dcSAndroid Build Coastguard Worker	if *check {
51*8fb009dcSAndroid Build Coastguard Worker		actual, err := os.ReadFile(dstPath)
52*8fb009dcSAndroid Build Coastguard Worker		if err != nil {
53*8fb009dcSAndroid Build Coastguard Worker			if os.IsNotExist(err) {
54*8fb009dcSAndroid Build Coastguard Worker				err = errors.New("missing file")
55*8fb009dcSAndroid Build Coastguard Worker			}
56*8fb009dcSAndroid Build Coastguard Worker			return err
57*8fb009dcSAndroid Build Coastguard Worker		}
58*8fb009dcSAndroid Build Coastguard Worker
59*8fb009dcSAndroid Build Coastguard Worker		if !bytes.Equal(expected, actual) {
60*8fb009dcSAndroid Build Coastguard Worker			return errors.New("file out of date")
61*8fb009dcSAndroid Build Coastguard Worker		}
62*8fb009dcSAndroid Build Coastguard Worker		return nil
63*8fb009dcSAndroid Build Coastguard Worker	}
64*8fb009dcSAndroid Build Coastguard Worker
65*8fb009dcSAndroid Build Coastguard Worker	if *dryRun {
66*8fb009dcSAndroid Build Coastguard Worker		fmt.Printf("Would write %d bytes to %q\n", len(expected), dst)
67*8fb009dcSAndroid Build Coastguard Worker		return nil
68*8fb009dcSAndroid Build Coastguard Worker	}
69*8fb009dcSAndroid Build Coastguard Worker
70*8fb009dcSAndroid Build Coastguard Worker	if err := os.MkdirAll(filepath.Dir(dstPath), 0777); err != nil {
71*8fb009dcSAndroid Build Coastguard Worker		return err
72*8fb009dcSAndroid Build Coastguard Worker	}
73*8fb009dcSAndroid Build Coastguard Worker	return os.WriteFile(dstPath, expected, 0666)
74*8fb009dcSAndroid Build Coastguard Worker}
75*8fb009dcSAndroid Build Coastguard Worker
76*8fb009dcSAndroid Build Coastguard Workertype taskError struct {
77*8fb009dcSAndroid Build Coastguard Worker	dst string
78*8fb009dcSAndroid Build Coastguard Worker	err error
79*8fb009dcSAndroid Build Coastguard Worker}
80*8fb009dcSAndroid Build Coastguard Worker
81*8fb009dcSAndroid Build Coastguard Workerfunc worker(taskChan <-chan Task, errorChan chan<- taskError, wg *sync.WaitGroup) {
82*8fb009dcSAndroid Build Coastguard Worker	defer wg.Done()
83*8fb009dcSAndroid Build Coastguard Worker	for t := range taskChan {
84*8fb009dcSAndroid Build Coastguard Worker		if err := runTask(t); err != nil {
85*8fb009dcSAndroid Build Coastguard Worker			errorChan <- taskError{t.Destination(), err}
86*8fb009dcSAndroid Build Coastguard Worker		}
87*8fb009dcSAndroid Build Coastguard Worker	}
88*8fb009dcSAndroid Build Coastguard Worker}
89*8fb009dcSAndroid Build Coastguard Worker
90*8fb009dcSAndroid Build Coastguard Workerfunc run() error {
91*8fb009dcSAndroid Build Coastguard Worker	if _, err := os.Stat("BUILDING.md"); err != nil {
92*8fb009dcSAndroid Build Coastguard Worker		return fmt.Errorf("must be run from BoringSSL source root")
93*8fb009dcSAndroid Build Coastguard Worker	}
94*8fb009dcSAndroid Build Coastguard Worker
95*8fb009dcSAndroid Build Coastguard Worker	buildJSON, err := os.ReadFile("build.json")
96*8fb009dcSAndroid Build Coastguard Worker	if err != nil {
97*8fb009dcSAndroid Build Coastguard Worker		return err
98*8fb009dcSAndroid Build Coastguard Worker	}
99*8fb009dcSAndroid Build Coastguard Worker
100*8fb009dcSAndroid Build Coastguard Worker	// Remove comments. For now, just do a very basic preprocessing step. If
101*8fb009dcSAndroid Build Coastguard Worker	// needed, we can switch to something well-defined like one of the many
102*8fb009dcSAndroid Build Coastguard Worker	// dozen different extended JSONs like JSON5.
103*8fb009dcSAndroid Build Coastguard Worker	lines := bytes.Split(buildJSON, []byte("\n"))
104*8fb009dcSAndroid Build Coastguard Worker	for i := range lines {
105*8fb009dcSAndroid Build Coastguard Worker		if idx := bytes.Index(lines[i], []byte("//")); idx >= 0 {
106*8fb009dcSAndroid Build Coastguard Worker			lines[i] = lines[i][:idx]
107*8fb009dcSAndroid Build Coastguard Worker		}
108*8fb009dcSAndroid Build Coastguard Worker	}
109*8fb009dcSAndroid Build Coastguard Worker	buildJSON = bytes.Join(lines, []byte("\n"))
110*8fb009dcSAndroid Build Coastguard Worker
111*8fb009dcSAndroid Build Coastguard Worker	var targetsIn map[string]InputTarget
112*8fb009dcSAndroid Build Coastguard Worker	if err := json.Unmarshal(buildJSON, &targetsIn); err != nil {
113*8fb009dcSAndroid Build Coastguard Worker		return fmt.Errorf("error decoding build config: %s", err)
114*8fb009dcSAndroid Build Coastguard Worker	}
115*8fb009dcSAndroid Build Coastguard Worker
116*8fb009dcSAndroid Build Coastguard Worker	var tasks []Task
117*8fb009dcSAndroid Build Coastguard Worker	targetsOut := make(map[string]build.Target)
118*8fb009dcSAndroid Build Coastguard Worker	for name, targetIn := range targetsIn {
119*8fb009dcSAndroid Build Coastguard Worker		targetOut, targetTasks, err := targetIn.Pregenerate(name)
120*8fb009dcSAndroid Build Coastguard Worker		if err != nil {
121*8fb009dcSAndroid Build Coastguard Worker			return err
122*8fb009dcSAndroid Build Coastguard Worker		}
123*8fb009dcSAndroid Build Coastguard Worker		targetsOut[name] = targetOut
124*8fb009dcSAndroid Build Coastguard Worker		tasks = append(tasks, targetTasks...)
125*8fb009dcSAndroid Build Coastguard Worker	}
126*8fb009dcSAndroid Build Coastguard Worker
127*8fb009dcSAndroid Build Coastguard Worker	tasks = append(tasks, MakeBuildFiles(targetsOut)...)
128*8fb009dcSAndroid Build Coastguard Worker	tasks = append(tasks, NewSimpleTask("gen/README.md", func() ([]byte, error) {
129*8fb009dcSAndroid Build Coastguard Worker		return []byte(readme), nil
130*8fb009dcSAndroid Build Coastguard Worker	}))
131*8fb009dcSAndroid Build Coastguard Worker
132*8fb009dcSAndroid Build Coastguard Worker	// Filter tasks by command-line argument.
133*8fb009dcSAndroid Build Coastguard Worker	if args := flag.Args(); len(args) != 0 {
134*8fb009dcSAndroid Build Coastguard Worker		var filtered []Task
135*8fb009dcSAndroid Build Coastguard Worker		for _, t := range tasks {
136*8fb009dcSAndroid Build Coastguard Worker			dst := t.Destination()
137*8fb009dcSAndroid Build Coastguard Worker			for _, arg := range args {
138*8fb009dcSAndroid Build Coastguard Worker				if strings.Contains(dst, arg) {
139*8fb009dcSAndroid Build Coastguard Worker					filtered = append(filtered, t)
140*8fb009dcSAndroid Build Coastguard Worker					break
141*8fb009dcSAndroid Build Coastguard Worker				}
142*8fb009dcSAndroid Build Coastguard Worker			}
143*8fb009dcSAndroid Build Coastguard Worker		}
144*8fb009dcSAndroid Build Coastguard Worker		tasks = filtered
145*8fb009dcSAndroid Build Coastguard Worker	}
146*8fb009dcSAndroid Build Coastguard Worker
147*8fb009dcSAndroid Build Coastguard Worker	if *list {
148*8fb009dcSAndroid Build Coastguard Worker		paths := make([]string, len(tasks))
149*8fb009dcSAndroid Build Coastguard Worker		for i, t := range tasks {
150*8fb009dcSAndroid Build Coastguard Worker			paths[i] = t.Destination()
151*8fb009dcSAndroid Build Coastguard Worker		}
152*8fb009dcSAndroid Build Coastguard Worker		slices.Sort(paths)
153*8fb009dcSAndroid Build Coastguard Worker		for _, p := range paths {
154*8fb009dcSAndroid Build Coastguard Worker			fmt.Println(p)
155*8fb009dcSAndroid Build Coastguard Worker		}
156*8fb009dcSAndroid Build Coastguard Worker		return nil
157*8fb009dcSAndroid Build Coastguard Worker	}
158*8fb009dcSAndroid Build Coastguard Worker
159*8fb009dcSAndroid Build Coastguard Worker	// Schedule tasks in parallel. Perlasm benefits from running in parallel. The
160*8fb009dcSAndroid Build Coastguard Worker	// others likely do not, but it is simpler to parallelize them all.
161*8fb009dcSAndroid Build Coastguard Worker	var wg sync.WaitGroup
162*8fb009dcSAndroid Build Coastguard Worker	taskChan := make(chan Task, *numWorkers)
163*8fb009dcSAndroid Build Coastguard Worker	errorChan := make(chan taskError, *numWorkers)
164*8fb009dcSAndroid Build Coastguard Worker	for i := 0; i < *numWorkers; i++ {
165*8fb009dcSAndroid Build Coastguard Worker		wg.Add(1)
166*8fb009dcSAndroid Build Coastguard Worker		go worker(taskChan, errorChan, &wg)
167*8fb009dcSAndroid Build Coastguard Worker	}
168*8fb009dcSAndroid Build Coastguard Worker
169*8fb009dcSAndroid Build Coastguard Worker	go func() {
170*8fb009dcSAndroid Build Coastguard Worker		for _, t := range tasks {
171*8fb009dcSAndroid Build Coastguard Worker			taskChan <- t
172*8fb009dcSAndroid Build Coastguard Worker		}
173*8fb009dcSAndroid Build Coastguard Worker		close(taskChan)
174*8fb009dcSAndroid Build Coastguard Worker		wg.Wait()
175*8fb009dcSAndroid Build Coastguard Worker		close(errorChan)
176*8fb009dcSAndroid Build Coastguard Worker	}()
177*8fb009dcSAndroid Build Coastguard Worker
178*8fb009dcSAndroid Build Coastguard Worker	var failed bool
179*8fb009dcSAndroid Build Coastguard Worker	for err := range errorChan {
180*8fb009dcSAndroid Build Coastguard Worker		fmt.Fprintf(os.Stderr, "Error in file %q: %s\n", err.dst, err.err)
181*8fb009dcSAndroid Build Coastguard Worker		failed = true
182*8fb009dcSAndroid Build Coastguard Worker	}
183*8fb009dcSAndroid Build Coastguard Worker	if failed {
184*8fb009dcSAndroid Build Coastguard Worker		return errors.New("some files had errors")
185*8fb009dcSAndroid Build Coastguard Worker	}
186*8fb009dcSAndroid Build Coastguard Worker	return nil
187*8fb009dcSAndroid Build Coastguard Worker}
188*8fb009dcSAndroid Build Coastguard Worker
189*8fb009dcSAndroid Build Coastguard Workerfunc main() {
190*8fb009dcSAndroid Build Coastguard Worker	flag.Parse()
191*8fb009dcSAndroid Build Coastguard Worker	if err := run(); err != nil {
192*8fb009dcSAndroid Build Coastguard Worker		fmt.Fprintf(os.Stderr, "Error: %s\n", err)
193*8fb009dcSAndroid Build Coastguard Worker		os.Exit(1)
194*8fb009dcSAndroid Build Coastguard Worker	}
195*8fb009dcSAndroid Build Coastguard Worker}
196*8fb009dcSAndroid Build Coastguard Worker
197*8fb009dcSAndroid Build Coastguard Workerconst readme = `# Pre-generated files
198*8fb009dcSAndroid Build Coastguard Worker
199*8fb009dcSAndroid Build Coastguard WorkerThis directory contains a number of pre-generated build artifacts. To simplify
200*8fb009dcSAndroid Build Coastguard Workerdownstream builds, they are checked into the repository, rather than dynamically
201*8fb009dcSAndroid Build Coastguard Workergenerated as part of the build.
202*8fb009dcSAndroid Build Coastguard Worker
203*8fb009dcSAndroid Build Coastguard WorkerWhen developing on BoringSSL, if any inputs to these files are modified, callers
204*8fb009dcSAndroid Build Coastguard Workermust run the following command to update the generated files:
205*8fb009dcSAndroid Build Coastguard Worker
206*8fb009dcSAndroid Build Coastguard Worker    go run ./util/pregenerate
207*8fb009dcSAndroid Build Coastguard Worker
208*8fb009dcSAndroid Build Coastguard WorkerTo check that files are up-to-date without updating files, run:
209*8fb009dcSAndroid Build Coastguard Worker
210*8fb009dcSAndroid Build Coastguard Worker    go run ./util/pregenerate -check
211*8fb009dcSAndroid Build Coastguard Worker
212*8fb009dcSAndroid Build Coastguard WorkerThis is run on CI to ensure the generated files remain up-to-date.
213*8fb009dcSAndroid Build Coastguard Worker
214*8fb009dcSAndroid Build Coastguard WorkerTo speed up local iteration, the tool accepts additional arguments to filter the
215*8fb009dcSAndroid Build Coastguard Workerfiles generated. For example, if editing ` + "`aesni-x86_64.pl`" + `, this
216*8fb009dcSAndroid Build Coastguard Workercommand will only update files with "aesni-x86_64" as a substring.
217*8fb009dcSAndroid Build Coastguard Worker
218*8fb009dcSAndroid Build Coastguard Worker    go run ./util/pregenerate aesni-x86_64
219*8fb009dcSAndroid Build Coastguard Worker
220*8fb009dcSAndroid Build Coastguard WorkerFor convenience, all files in this directory, including this README, are managed
221*8fb009dcSAndroid Build Coastguard Workerby the tool. This means the whole directory may be deleted and regenerated from
222*8fb009dcSAndroid Build Coastguard Workerscratch at any time.
223*8fb009dcSAndroid Build Coastguard Worker`
224