| #!/usr/bin/env vpython3 |
| # Copyright 2014 The Chromium Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| """Python wrapper for various Go tools. |
| |
| Intended to be used from a PRESUBMIT check. |
| """ |
| |
| import os |
| import subprocess |
| import sys |
| |
| WORKSPACE_ROOT = os.path.dirname(os.path.abspath(__file__)) |
| |
| |
| def group_by_dir(filestream): |
| """Generates directories of the files in the given filestream. |
| |
| If the filestream has paths in sorted order, then directories |
| shouldn't be repeated. |
| |
| Skips files that reside in a "testdata" directory. |
| """ |
| prefix = None |
| group = [] |
| for fname in filestream: |
| dirname = os.path.dirname(fname) |
| if '/testdata/' in os.path.normpath(dirname): |
| continue |
| if dirname != prefix: |
| if group: |
| yield prefix |
| prefix = dirname |
| group = [] |
| group.append(fname) |
| if group: |
| yield prefix |
| |
| |
| def mk_checker(*tool_name): |
| """Creates and returns a "main" function. |
| |
| The returned function invokes the tool and returns the retcode-style result. |
| """ |
| tool_cmd = list(tool_name) |
| |
| def _inner(_verbose, filestream): |
| found_errs = [] |
| retcode = 0 |
| |
| for dpath in group_by_dir(filestream): |
| proc = subprocess.Popen( |
| tool_cmd + ['.'], |
| cwd=dpath, |
| stdout=subprocess.PIPE, |
| stderr=subprocess.STDOUT, |
| universal_newlines=True) |
| out = proc.communicate()[0].strip() |
| if proc.returncode and 'build constraints exclude all Go files' in out: |
| continue |
| if out or proc.returncode: |
| found_errs.append(out or 'Unrecognized error') |
| retcode = proc.returncode or 1 |
| |
| for err in found_errs: |
| print(err) |
| return retcode |
| return _inner |
| |
| |
| def gofmt_main(verbose, filestream): |
| """Reads list of paths from stdin. |
| |
| Expects go toolset to be in PATH (use ./env.py to set this up). |
| """ |
| def check_file(path): |
| proc = subprocess.Popen( |
| ['gofmt', '-s', '-d', path], |
| stdout=subprocess.PIPE, |
| stderr=subprocess.PIPE) |
| out, err = proc.communicate() |
| if proc.returncode: |
| print(err, end=' ') |
| return False |
| if out: |
| if verbose: |
| print(out, end=' ') |
| return False |
| return True |
| |
| bad = [] |
| for fpath in filestream: |
| if not check_file(fpath): |
| bad.append(fpath) |
| if bad: |
| root = WORKSPACE_ROOT.rstrip(os.sep) + os.sep |
| print('Badly formated Go files:') |
| for p in bad: |
| if p.startswith(root): |
| p = p[len(root):] |
| print(' %s' % p) |
| print() |
| print('Consider running \'gofmt -s -w /path/to/infra\'') |
| return 0 if not bad else 1 |
| |
| |
| def show_help(): |
| print('Usage: check.py [--verbose] <tool>') |
| print('List of file paths to operate is read from stdin.') |
| print('Available tools:') |
| for x in TOOL_FUNC: |
| print(" *", x) |
| sys.exit(1) |
| |
| |
| def main(args): |
| if len(args) < 1 or args[0] not in TOOL_FUNC: |
| show_help() |
| |
| verbose = '--verbose' in args |
| |
| def filestream(): |
| for path in sys.stdin: |
| path = path.rstrip() |
| if path: |
| yield path |
| |
| return TOOL_FUNC[args[0]](verbose, filestream()) |
| |
| |
| TOOL_FUNC = { |
| # TODO(gregorynisbet): tricium copies around proto messages. |
| # Disable copylock analysis for now. |
| "govet": mk_checker("go", "vet", "-copylocks=false"), |
| "gofmt": gofmt_main, |
| } |
| |
| |
| if __name__ == '__main__': |
| sys.exit(main(sys.argv[1:])) |