pre-upload.py: support per-project checkpatch.pl

Projects often carry their own checkpatch.pl, and the imprecise and
always-changing nature of the script means it's best (e.g., for false
negatives/positives) to match the local project with its script.

Allow that in PRESUBMIT.cfg, e.g.:

  # kernel project
  [Hook Overrides Options]
  checkpatch_check: ./scripts/checkpatch.pl

  # coreboot
  [Hook Overrides Options]
  checkpatch_check: ./util/lint/checkpatch.pl --some=other --options

BUG=none
TEST=pre-upload.py on coreboot, kernel, etc., with and without .cfg
     changes

Change-Id: I948188a98bd7c788d6a951dccd0928d76bdc4f0c
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/repohooks/+/2243934
Tested-by: Brian Norris <briannorris@chromium.org>
Reviewed-by: Mike Frysinger <vapier@chromium.org>
Commit-Queue: Brian Norris <briannorris@chromium.org>
diff --git a/pre-upload.py b/pre-upload.py
index d1ebb27..182123e 100755
--- a/pre-upload.py
+++ b/pre-upload.py
@@ -1481,6 +1481,10 @@
   """Runs checkpatch.pl on the given project"""
   hooks_dir = _get_hooks_dir()
   options = list(options)
+  if options and options[0].startswith('./') and os.path.exists(options[0]):
+    cmdpath = options.pop(0)
+  else:
+    cmdpath = os.path.join(hooks_dir, 'checkpatch.pl')
   if commit == PRE_SUBMIT:
     # The --ignore option must be present and include 'MISSING_SIGN_OFF' in
     # this case.
@@ -1493,13 +1497,14 @@
   # Upstream does not want those lines (since they do not use Gerrit), but
   # we always do, so disable the check globally.
   options.append('--ignore=GERRIT_CHANGE_ID')
-  cmd = [os.path.join(hooks_dir, 'checkpatch.pl')] + options + ['-']
+  cmd = [cmdpath] + options + ['-']
   cmd_result = cros_build_lib.run(
       cmd, print_cmd=False, input=_get_patch(commit).encode('utf-8'),
       stdout=True, stderr=subprocess.STDOUT, check=False, encoding='utf-8',
       errors='replace')
   if cmd_result.returncode:
-    return HookFailure('checkpatch.pl errors/warnings\n\n' + cmd_result.stdout)
+    return HookFailure('%s errors/warnings\n\n%s' %
+                       (cmdpath, cmd_result.stdout))
   return None