| // Copyright 2020 The SwiftShader Authors. All Rights Reserved. | |
| // | |
| // Licensed under the Apache License, Version 2.0 (the "License"); | |
| // you may not use this file except in compliance with the License. | |
| // You may obtain a copy of the License at | |
| // | |
| // http://www.apache.org/licenses/LICENSE-2.0 | |
| // | |
| // Unless required by applicable law or agreed to in writing, software | |
| // distributed under the License is distributed on an "AS IS" BASIS, | |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| // See the License for the specific language governing permissions and | |
| // limitations under the License. | |
| // check_build_files scans all the .bp, .gn and .bazel files for source | |
| // references to non-existent files. | |
| package main | |
| import ( | |
| "flag" | |
| "fmt" | |
| "io/ioutil" | |
| "os" | |
| "path/filepath" | |
| "regexp" | |
| "strings" | |
| ) | |
| func cwd() string { | |
| wd, err := os.Getwd() | |
| if err != nil { | |
| return "" | |
| } | |
| return wd | |
| } | |
| var root = flag.String("root", cwd(), "root project directory") | |
| func main() { | |
| flag.Parse() | |
| if err := run(); err != nil { | |
| fmt.Fprintf(os.Stderr, "%v", err) | |
| os.Exit(1) | |
| } | |
| fmt.Printf("Build file check completed with no errors\n") | |
| } | |
| func run() error { | |
| wd := *root | |
| errs := []error{} | |
| filepath.Walk(wd, func(path string, info os.FileInfo, err error) error { | |
| if err != nil { | |
| return err | |
| } | |
| rel, err := filepath.Rel(wd, path) | |
| if err != nil { | |
| return filepath.SkipDir | |
| } | |
| switch rel { | |
| case ".git", "cache", "build", "out", "third_party": | |
| return filepath.SkipDir | |
| } | |
| if info.IsDir() { | |
| return nil | |
| } | |
| content, err := ioutil.ReadFile(path) | |
| if err != nil { | |
| errs = append(errs, err) | |
| return nil // Continue walking files | |
| } | |
| switch filepath.Ext(path) { | |
| case ".bp": | |
| errs = append(errs, checkBlueprint(path, string(content))...) | |
| case ".gn": | |
| errs = append(errs, checkGn(path, string(content))...) | |
| case ".bazel": | |
| errs = append(errs, checkBazel(path, string(content))...) | |
| } | |
| return nil | |
| }) | |
| sb := strings.Builder{} | |
| for _, err := range errs { | |
| sb.WriteString(err.Error()) | |
| sb.WriteString("\n") | |
| } | |
| if sb.Len() > 0 { | |
| return fmt.Errorf("%v", sb.String()) | |
| } | |
| return nil | |
| } | |
| var ( | |
| reSources = regexp.MustCompile(`sources\s*=\s*\[([^\]]*)\]`) | |
| reSrc = regexp.MustCompile(`srcs\s*[:=]\s*\[([^\]]*)\]`) | |
| reQuoted = regexp.MustCompile(`"([^\"]*)"`) | |
| ) | |
| func checkBlueprint(path, content string) []error { | |
| errs := []error{} | |
| for _, sources := range matchRE(reSrc, content) { | |
| for _, source := range matchRE(reQuoted, sources) { | |
| if strings.HasPrefix(source, ":") { | |
| continue // Build target, we can't resolve. | |
| } | |
| if err := checkSource(path, source); err != nil { | |
| errs = append(errs, err) | |
| } | |
| } | |
| } | |
| return errs | |
| } | |
| func checkGn(path, content string) []error { | |
| errs := []error{} | |
| for _, sources := range matchRE(reSources, content) { | |
| for _, source := range matchRE(reQuoted, sources) { | |
| if strings.ContainsAny(source, "$") { | |
| return nil // Env vars we can't resolve | |
| } | |
| if strings.HasPrefix(source, "//") { | |
| continue // Build target, we can't resolve. | |
| } | |
| if err := checkSource(path, source); err != nil { | |
| errs = append(errs, err) | |
| } | |
| } | |
| } | |
| return errs | |
| } | |
| func checkBazel(path, content string) []error { | |
| errs := []error{} | |
| for _, sources := range matchRE(reSrc, content) { | |
| for _, source := range matchRE(reQuoted, sources) { | |
| if strings.HasPrefix(source, "@") || strings.HasPrefix(source, ":") { | |
| continue // Build target, we can't resolve. | |
| } | |
| if err := checkSource(path, source); err != nil { | |
| errs = append(errs, err) | |
| } | |
| } | |
| } | |
| return errs | |
| } | |
| func checkSource(path, source string) error { | |
| source = filepath.Join(filepath.Dir(path), source) | |
| if strings.Contains(source, "*") { | |
| sources, err := filepath.Glob(source) | |
| if err != nil { | |
| return fmt.Errorf("In '%v': %w", path, err) | |
| } | |
| if len(sources) == 0 { | |
| return fmt.Errorf("In '%v': Glob '%v' does not reference any files", path, source) | |
| } | |
| return nil | |
| } | |
| stat, err := os.Stat(source) | |
| if err != nil { | |
| return fmt.Errorf("In '%v': %w", path, err) | |
| } | |
| if stat.IsDir() { | |
| return fmt.Errorf("In '%v': '%v' refers to a directory, not a file", path, source) | |
| } | |
| return nil | |
| } | |
| func matchRE(re *regexp.Regexp, text string) []string { | |
| out := []string{} | |
| for _, match := range re.FindAllStringSubmatch(text, -1) { | |
| if len(match) < 2 { | |
| return nil | |
| } | |
| out = append(out, match[1]) | |
| } | |
| return out | |
| } |