1// Copyright 2020 The SwiftShader Authors. All Rights Reserved. 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15// check_build_files scans all the .bp, .gn and .bazel files for source 16// references to non-existent files. 17 18package main 19 20import ( 21 "flag" 22 "fmt" 23 "io/ioutil" 24 "os" 25 "path/filepath" 26 "regexp" 27 "strings" 28) 29 30func cwd() string { 31 wd, err := os.Getwd() 32 if err != nil { 33 return "" 34 } 35 return wd 36} 37 38var root = flag.String("root", cwd(), "root project directory") 39 40func main() { 41 flag.Parse() 42 43 if err := run(); err != nil { 44 fmt.Fprintf(os.Stderr, "%v", err) 45 os.Exit(1) 46 } 47 fmt.Printf("Build file check completed with no errors\n") 48} 49 50func run() error { 51 wd := *root 52 53 errs := []error{} 54 55 filepath.Walk(wd, func(path string, info os.FileInfo, err error) error { 56 if err != nil { 57 return err 58 } 59 60 rel, err := filepath.Rel(wd, path) 61 if err != nil { 62 return filepath.SkipDir 63 } 64 65 switch rel { 66 case ".git", "cache", "build", "out", "third_party": 67 return filepath.SkipDir 68 } 69 70 if info.IsDir() { 71 return nil 72 } 73 74 content, err := ioutil.ReadFile(path) 75 if err != nil { 76 errs = append(errs, err) 77 return nil // Continue walking files 78 } 79 80 switch filepath.Ext(path) { 81 case ".bp": 82 errs = append(errs, checkBlueprint(path, string(content))...) 83 case ".gn": 84 errs = append(errs, checkGn(path, string(content))...) 85 case ".bazel": 86 errs = append(errs, checkBazel(path, string(content))...) 87 } 88 89 return nil 90 }) 91 92 sb := strings.Builder{} 93 for _, err := range errs { 94 sb.WriteString(err.Error()) 95 sb.WriteString("\n") 96 } 97 if sb.Len() > 0 { 98 return fmt.Errorf("%v", sb.String()) 99 } 100 return nil 101} 102 103var ( 104 reSources = regexp.MustCompile(`sources\s*=\s*\[([^\]]*)\]`) 105 reSrc = regexp.MustCompile(`srcs\s*[:=]\s*\[([^\]]*)\]`) 106 reQuoted = regexp.MustCompile(`"([^\"]*)"`) 107) 108 109func checkBlueprint(path, content string) []error { 110 errs := []error{} 111 for _, sources := range matchRE(reSrc, content) { 112 for _, source := range matchRE(reQuoted, sources) { 113 if strings.HasPrefix(source, ":") { 114 continue // Build target, we can't resolve. 115 } 116 if err := checkSource(path, source); err != nil { 117 errs = append(errs, err) 118 } 119 } 120 } 121 return errs 122} 123 124func checkGn(path, content string) []error { 125 errs := []error{} 126 for _, sources := range matchRE(reSources, content) { 127 for _, source := range matchRE(reQuoted, sources) { 128 if strings.ContainsAny(source, "$") { 129 return nil // Env vars we can't resolve 130 } 131 if strings.HasPrefix(source, "//") { 132 continue // Build target, we can't resolve. 133 } 134 if err := checkSource(path, source); err != nil { 135 errs = append(errs, err) 136 } 137 } 138 } 139 return errs 140} 141 142func checkBazel(path, content string) []error { 143 errs := []error{} 144 for _, sources := range matchRE(reSrc, content) { 145 for _, source := range matchRE(reQuoted, sources) { 146 if strings.HasPrefix(source, "@") || strings.HasPrefix(source, ":") { 147 continue // Build target, we can't resolve. 148 } 149 if err := checkSource(path, source); err != nil { 150 errs = append(errs, err) 151 } 152 } 153 } 154 return errs 155} 156 157func checkSource(path, source string) error { 158 source = filepath.Join(filepath.Dir(path), source) 159 160 if strings.Contains(source, "*") { 161 sources, err := filepath.Glob(source) 162 if err != nil { 163 return fmt.Errorf("In '%v': %w", path, err) 164 } 165 if len(sources) == 0 { 166 return fmt.Errorf("In '%v': Glob '%v' does not reference any files", path, source) 167 } 168 return nil 169 } 170 171 stat, err := os.Stat(source) 172 if err != nil { 173 return fmt.Errorf("In '%v': %w", path, err) 174 } 175 if stat.IsDir() { 176 return fmt.Errorf("In '%v': '%v' refers to a directory, not a file", path, source) 177 } 178 return nil 179} 180 181func matchRE(re *regexp.Regexp, text string) []string { 182 out := []string{} 183 for _, match := range re.FindAllStringSubmatch(text, -1) { 184 if len(match) < 2 { 185 return nil 186 } 187 out = append(out, match[1]) 188 } 189 return out 190} 191