diff --git a/PRESUBMIT.py b/PRESUBMIT.py index 8e03359dfe..7c93279e12 100644 --- a/PRESUBMIT.py +++ b/PRESUBMIT.py @@ -127,8 +127,29 @@ def _NonInclusiveFileFilter(file): return file.LocalPath() not in filter_list +def _CheckNoStaleGen(input_api, output_api): + results = [] + try: + go = input_api.os_path.join(input_api.change.RepositoryRoot(), "tools", + "golang", "bin", "go") + if input_api.is_windows: + go += '.exe' + input_api.subprocess.check_call_out( + [go, "run", "tools/src/cmd/gen/main.go", "--check-stale"], + stdout=input_api.subprocess.PIPE, + stderr=input_api.subprocess.PIPE, + cwd=input_api.change.RepositoryRoot()) + except input_api.subprocess.CalledProcessError as e: + if input_api.is_committing: + results.append(output_api.PresubmitError('%s' % (e, ))) + else: + results.append(output_api.PresubmitPromptWarning('%s' % (e, ))) + return results + + def _DoCommonChecks(input_api, output_api): results = [] + results.extend(_CheckNoStaleGen(input_api, output_api)) results.extend( input_api.canned_checks.CheckChangedLUCIConfigs(input_api, output_api)) diff --git a/tools/src/cmd/gen/main.go b/tools/src/cmd/gen/main.go index f1ba9c3626..653adbbbfc 100644 --- a/tools/src/cmd/gen/main.go +++ b/tools/src/cmd/gen/main.go @@ -75,10 +75,13 @@ optional flags:`) func run() error { outputDir := "" verbose := false + checkStale := false flag.StringVar(&outputDir, "o", "", "custom output directory (optional)") flag.BoolVar(&verbose, "verbose", false, "print verbose output") + flag.BoolVar(&checkStale, "check-stale", false, "don't emit anything, just check that files are up to date") flag.Parse() + staleFiles := []string{} projectRoot := fileutils.DawnRoot() // Find clang-format @@ -168,8 +171,22 @@ func run() error { sb := strings.Builder{} sb.WriteString(fmt.Sprintf(header, copyrightYear, filepath.ToSlash(relTmplPath))) sb.WriteString(body) - content := sb.String() - return writeFileIfChanged(outPath, content, string(existing)) + oldContent, newContent := string(existing), sb.String() + + if oldContent != newContent { + if checkStale { + staleFiles = append(staleFiles, outPath) + } else { + if err := os.MkdirAll(filepath.Dir(outPath), 0777); err != nil { + return fmt.Errorf("failed to create directory for '%v': %w", outPath, err) + } + if err := ioutil.WriteFile(outPath, []byte(newContent), 0666); err != nil { + return fmt.Errorf("failed to write file '%v': %w", outPath, err) + } + } + } + + return nil } // Write the content generated using the template and semantic info @@ -196,6 +213,19 @@ func run() error { } } + if len(staleFiles) > 0 { + fmt.Println(len(staleFiles), "files need regenerating:") + for _, path := range staleFiles { + if rel, err := filepath.Rel(projectRoot, path); err == nil { + fmt.Println(" •", rel) + } else { + fmt.Println(" •", path) + } + } + fmt.Println("Regenerate these files with: ./tools/run gen") + os.Exit(1) + } + return nil } @@ -268,20 +298,6 @@ func (g *genCache) permute(overload *sem.Overload) ([]gen.Permutation, error) { return g.cached.permuter.Permute(overload) } -// writes content to path if the file has changed -func writeFileIfChanged(path, newContent, oldContent string) error { - if oldContent == newContent { - return nil // Not changed - } - if err := os.MkdirAll(filepath.Dir(path), 0777); err != nil { - return fmt.Errorf("failed to create directory for '%v': %w", path, err) - } - if err := ioutil.WriteFile(path, []byte(newContent), 0666); err != nil { - return fmt.Errorf("failed to write file '%v': %w", path, err) - } - return nil -} - var copyrightRegex = regexp.MustCompile(`// Copyright (\d+) The`) const header = `// Copyright %v The Tint Authors.