xref: /aosp_15_r20/build/make/tools/canoninja/canoninja.go (revision 9e94795a3d4ef5c1d47486f9a02bb378756cea8a)
1*9e94795aSAndroid Build Coastguard Workerpackage canoninja
2*9e94795aSAndroid Build Coastguard Worker
3*9e94795aSAndroid Build Coastguard Workerimport (
4*9e94795aSAndroid Build Coastguard Worker	"bytes"
5*9e94795aSAndroid Build Coastguard Worker	"crypto/sha1"
6*9e94795aSAndroid Build Coastguard Worker	"encoding/hex"
7*9e94795aSAndroid Build Coastguard Worker	"fmt"
8*9e94795aSAndroid Build Coastguard Worker	"io"
9*9e94795aSAndroid Build Coastguard Worker)
10*9e94795aSAndroid Build Coastguard Worker
11*9e94795aSAndroid Build Coastguard Workervar (
12*9e94795aSAndroid Build Coastguard Worker	rulePrefix  = []byte("rule ")
13*9e94795aSAndroid Build Coastguard Worker	buildPrefix = []byte("build ")
14*9e94795aSAndroid Build Coastguard Worker	phonyRule   = []byte("phony")
15*9e94795aSAndroid Build Coastguard Worker)
16*9e94795aSAndroid Build Coastguard Worker
17*9e94795aSAndroid Build Coastguard Workerfunc Generate(path string, buffer []byte, sink io.Writer) error {
18*9e94795aSAndroid Build Coastguard Worker	// Break file into lines
19*9e94795aSAndroid Build Coastguard Worker	from := 0
20*9e94795aSAndroid Build Coastguard Worker	var lines [][]byte
21*9e94795aSAndroid Build Coastguard Worker	for from < len(buffer) {
22*9e94795aSAndroid Build Coastguard Worker		line := getLine(buffer[from:])
23*9e94795aSAndroid Build Coastguard Worker		lines = append(lines, line)
24*9e94795aSAndroid Build Coastguard Worker		from += len(line)
25*9e94795aSAndroid Build Coastguard Worker	}
26*9e94795aSAndroid Build Coastguard Worker
27*9e94795aSAndroid Build Coastguard Worker	// FOr each rule, calculate and remember its digest
28*9e94795aSAndroid Build Coastguard Worker	ruleDigest := make(map[string]string)
29*9e94795aSAndroid Build Coastguard Worker	for i := 0; i < len(lines); {
30*9e94795aSAndroid Build Coastguard Worker		if bytes.HasPrefix(lines[i], rulePrefix) {
31*9e94795aSAndroid Build Coastguard Worker			// Find ruleName
32*9e94795aSAndroid Build Coastguard Worker			rn := ruleName(lines[i])
33*9e94795aSAndroid Build Coastguard Worker			if len(rn) == 0 {
34*9e94795aSAndroid Build Coastguard Worker				return fmt.Errorf("%s:%d: rule name is missing or on the next line", path, i+1)
35*9e94795aSAndroid Build Coastguard Worker			}
36*9e94795aSAndroid Build Coastguard Worker			sRuleName := string(rn)
37*9e94795aSAndroid Build Coastguard Worker			if _, ok := ruleDigest[sRuleName]; ok {
38*9e94795aSAndroid Build Coastguard Worker				return fmt.Errorf("%s:%d: the rule %s has been already defined", path, i+1, sRuleName)
39*9e94795aSAndroid Build Coastguard Worker			}
40*9e94795aSAndroid Build Coastguard Worker			// Calculate rule text digest as a digests of line digests.
41*9e94795aSAndroid Build Coastguard Worker			var digests []byte
42*9e94795aSAndroid Build Coastguard Worker			doDigest := func(b []byte) {
43*9e94795aSAndroid Build Coastguard Worker				h := sha1.New()
44*9e94795aSAndroid Build Coastguard Worker				h.Write(b)
45*9e94795aSAndroid Build Coastguard Worker				digests = h.Sum(digests)
46*9e94795aSAndroid Build Coastguard Worker
47*9e94795aSAndroid Build Coastguard Worker			}
48*9e94795aSAndroid Build Coastguard Worker			// For the first line, digest everything after rule's name
49*9e94795aSAndroid Build Coastguard Worker			doDigest(lines[i][cap(lines[i])+len(rn)-cap(rn):])
50*9e94795aSAndroid Build Coastguard Worker			for i++; i < len(lines) && lines[i][0] == ' '; i++ {
51*9e94795aSAndroid Build Coastguard Worker				doDigest(lines[i])
52*9e94795aSAndroid Build Coastguard Worker			}
53*9e94795aSAndroid Build Coastguard Worker			h := sha1.New()
54*9e94795aSAndroid Build Coastguard Worker			h.Write(digests)
55*9e94795aSAndroid Build Coastguard Worker			ruleDigest[sRuleName] = "R" + hex.EncodeToString(h.Sum(nil))
56*9e94795aSAndroid Build Coastguard Worker
57*9e94795aSAndroid Build Coastguard Worker		} else {
58*9e94795aSAndroid Build Coastguard Worker			i++
59*9e94795aSAndroid Build Coastguard Worker		}
60*9e94795aSAndroid Build Coastguard Worker	}
61*9e94795aSAndroid Build Coastguard Worker
62*9e94795aSAndroid Build Coastguard Worker	// Rewrite rule names.
63*9e94795aSAndroid Build Coastguard Worker	for i, line := range lines {
64*9e94795aSAndroid Build Coastguard Worker		if bytes.HasPrefix(line, buildPrefix) {
65*9e94795aSAndroid Build Coastguard Worker			brn := getBuildRuleName(line)
66*9e94795aSAndroid Build Coastguard Worker			if bytes.Equal(brn, phonyRule) {
67*9e94795aSAndroid Build Coastguard Worker				sink.Write(line)
68*9e94795aSAndroid Build Coastguard Worker				continue
69*9e94795aSAndroid Build Coastguard Worker			}
70*9e94795aSAndroid Build Coastguard Worker			if len(brn) == 0 {
71*9e94795aSAndroid Build Coastguard Worker				return fmt.Errorf("%s:%d: build statement lacks rule name", path, i+1)
72*9e94795aSAndroid Build Coastguard Worker			}
73*9e94795aSAndroid Build Coastguard Worker			sink.Write(line[0 : cap(line)-cap(brn)])
74*9e94795aSAndroid Build Coastguard Worker			if digest, ok := ruleDigest[string(brn)]; ok {
75*9e94795aSAndroid Build Coastguard Worker				sink.Write([]byte(digest))
76*9e94795aSAndroid Build Coastguard Worker			} else {
77*9e94795aSAndroid Build Coastguard Worker				return fmt.Errorf("%s:%d: no rule for this build target", path, i+1)
78*9e94795aSAndroid Build Coastguard Worker			}
79*9e94795aSAndroid Build Coastguard Worker			sink.Write(line[cap(line)+len(brn)-cap(brn):])
80*9e94795aSAndroid Build Coastguard Worker		} else if bytes.HasPrefix(line, rulePrefix) {
81*9e94795aSAndroid Build Coastguard Worker			rn := ruleName(line)
82*9e94795aSAndroid Build Coastguard Worker			// Write everything before it
83*9e94795aSAndroid Build Coastguard Worker			sink.Write(line[0 : cap(line)-cap(rn)])
84*9e94795aSAndroid Build Coastguard Worker			sink.Write([]byte(ruleDigest[string(rn)]))
85*9e94795aSAndroid Build Coastguard Worker			sink.Write(line[cap(line)+len(rn)-cap(rn):])
86*9e94795aSAndroid Build Coastguard Worker		} else {
87*9e94795aSAndroid Build Coastguard Worker			//goland:noinspection GoUnhandledErrorResult
88*9e94795aSAndroid Build Coastguard Worker			sink.Write(line)
89*9e94795aSAndroid Build Coastguard Worker		}
90*9e94795aSAndroid Build Coastguard Worker	}
91*9e94795aSAndroid Build Coastguard Worker	return nil
92*9e94795aSAndroid Build Coastguard Worker}
93*9e94795aSAndroid Build Coastguard Worker
94*9e94795aSAndroid Build Coastguard Workerfunc getLine(b []byte) []byte {
95*9e94795aSAndroid Build Coastguard Worker	if n := bytes.IndexByte(b, '\n'); n >= 0 {
96*9e94795aSAndroid Build Coastguard Worker		return b[:n+1]
97*9e94795aSAndroid Build Coastguard Worker	}
98*9e94795aSAndroid Build Coastguard Worker	return b
99*9e94795aSAndroid Build Coastguard Worker}
100*9e94795aSAndroid Build Coastguard Worker
101*9e94795aSAndroid Build Coastguard Worker// Returns build statement's rule name
102*9e94795aSAndroid Build Coastguard Workerfunc getBuildRuleName(line []byte) []byte {
103*9e94795aSAndroid Build Coastguard Worker	n := bytes.IndexByte(line, ':')
104*9e94795aSAndroid Build Coastguard Worker	if n <= 0 {
105*9e94795aSAndroid Build Coastguard Worker		return nil
106*9e94795aSAndroid Build Coastguard Worker	}
107*9e94795aSAndroid Build Coastguard Worker	ruleName := line[n+1:]
108*9e94795aSAndroid Build Coastguard Worker	if ruleName[0] == ' ' {
109*9e94795aSAndroid Build Coastguard Worker		ruleName = bytes.TrimLeft(ruleName, " ")
110*9e94795aSAndroid Build Coastguard Worker	}
111*9e94795aSAndroid Build Coastguard Worker	if n := bytes.IndexAny(ruleName, " \t\r\n"); n >= 0 {
112*9e94795aSAndroid Build Coastguard Worker		ruleName = ruleName[0:n]
113*9e94795aSAndroid Build Coastguard Worker	}
114*9e94795aSAndroid Build Coastguard Worker	return ruleName
115*9e94795aSAndroid Build Coastguard Worker}
116*9e94795aSAndroid Build Coastguard Worker
117*9e94795aSAndroid Build Coastguard Worker// Returns rule statement's rule name
118*9e94795aSAndroid Build Coastguard Workerfunc ruleName(lineAfterRule []byte) []byte {
119*9e94795aSAndroid Build Coastguard Worker	ruleName := lineAfterRule[len(rulePrefix):]
120*9e94795aSAndroid Build Coastguard Worker	if len(ruleName) == 0 {
121*9e94795aSAndroid Build Coastguard Worker		return ruleName
122*9e94795aSAndroid Build Coastguard Worker	}
123*9e94795aSAndroid Build Coastguard Worker	if ruleName[0] == ' ' {
124*9e94795aSAndroid Build Coastguard Worker		ruleName = bytes.TrimLeft(ruleName, " ")
125*9e94795aSAndroid Build Coastguard Worker	}
126*9e94795aSAndroid Build Coastguard Worker	if n := bytes.IndexAny(ruleName, " \t\r\n"); n >= 0 {
127*9e94795aSAndroid Build Coastguard Worker		ruleName = ruleName[0:n]
128*9e94795aSAndroid Build Coastguard Worker	}
129*9e94795aSAndroid Build Coastguard Worker	return ruleName
130*9e94795aSAndroid Build Coastguard Worker}
131