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