Get clang_tidy check running.

Running, but not passing. Also, presubmit steps that are not yet
passing can be added to the "broken" program so they can be easily
tested.

I think it needs '-warnings-as-errors' but I get errors when adding
that. It also complains about '-Wno-psabi'.

Change-Id: I72d92cce16f6a764b523d83d36280d6ce3350440
Bug: 45
diff --git a/env_setup/cipd/update.py b/env_setup/cipd/update.py
index 74c1f22..53ba1aa 100755
--- a/env_setup/cipd/update.py
+++ b/env_setup/cipd/update.py
@@ -90,11 +90,16 @@
         os.makedirs(args.install_dir)
 
     paths = []
+    env = {
+        'CIPD_INSTALL_DIR': args.install_dir,
+        'CIPD_CACHE_DIR': args.cache_dir,
+    }
 
     default_ensures = os.path.join(SCRIPT_ROOT, '*.ensure')
     for ensure_file in args.ensure_file or glob.glob(default_ensures):
         install_dir = os.path.join(args.install_dir,
                                    os.path.basename(ensure_file))
+
         cmd = [
             args.cipd,
             'ensure',
@@ -110,6 +115,12 @@
         paths.append(install_dir)
         paths.append(os.path.join(install_dir, 'bin'))
 
+        name = ensure_file
+        if os.path.splitext(name)[1] == '.ensure':
+            name = os.path.splitext(name)[0]
+        name = os.path.basename(name)
+        env['{}_CIPD_INSTALL_DIR'.format(name.upper())] = install_dir
+
     for path in paths:
         print('adding {} to path'.format(path), file=sys.stderr)
 
@@ -121,10 +132,9 @@
                                          prefix='cipdsetup') as temp:
             print('PATH="{}"'.format(os.pathsep.join(paths)), file=temp)
             print('export PATH', file=temp)
-            print('CIPD_INSTALL_DIR="{}"'.format(args.install_dir), file=temp)
-            print('export CIPD_INSTALL_DIR', file=temp)
-            print('CIPD_CACHE_DIR={}'.format(args.cache_dir), file=temp)
-            print('export CIPD_CACHE_DIR', file=temp)
+            for name, value in env.items():
+                print('{}={}'.format(name, value), file=temp)
+                print('export {}'.format(name), file=temp)
 
             print('. {}'.format(temp.name))
 
diff --git a/pw_presubmit/py/pw_presubmit/pigweed_presubmit.py b/pw_presubmit/py/pw_presubmit/pigweed_presubmit.py
index 669e995..c526a53 100755
--- a/pw_presubmit/py/pw_presubmit/pigweed_presubmit.py
+++ b/pw_presubmit/py/pw_presubmit/pigweed_presubmit.py
@@ -16,6 +16,7 @@
 """Runs the local presubmit checks for the Pigweed repository."""
 
 import argparse
+import itertools
 import logging
 import os
 from pathlib import Path
@@ -108,10 +109,11 @@
          **kwargs)
 
 
-def ninja(ctx: PresubmitContext, **kwargs) -> None:
+def ninja(*args, ctx: PresubmitContext, **kwargs):
     call('ninja',
          '-C',
          ctx.output_directory,
+         *args,
          cwd=ctx.repository_root,
          **kwargs)
 
@@ -121,7 +123,7 @@
 
 
 def gn_clang_build(ctx: PresubmitContext):
-    gn_gen('--export-compile-commands', _CLANG_GEN_ARGS, ctx=ctx)
+    gn_gen(_CLANG_GEN_ARGS, ctx=ctx)
     ninja(ctx=ctx)
 
 
@@ -149,24 +151,41 @@
     gn_arm_build,
 )
 
-
 #
 # C++ presubmit checks
 #
+# TODO(pwbug/45) Probably want additional checks.
+CLANG_TIDY_CHECKS = ('modernize-use-override', )
+
+
 @filter_paths(endswith=format_code.C_FORMAT.extensions)
 def clang_tidy(ctx: PresubmitContext):
-    # TODO(mohrr) should this check just do a new clang build?
-    out = ctx.output_directory.joinpath('..', 'gn_clang_build')
-    if not out.joinpath('compile_commands.json').exists():
-        raise PresubmitFailure('clang_tidy MUST be run after generating '
-                               'compile_commands.json in a clang build!')
+    gn_gen('--export-compile-commands', _CLANG_GEN_ARGS, ctx=ctx)
+    ninja(ctx=ctx)
+    ninja('-t', 'compdb', 'objcxx', 'cxx', ctx=ctx)
 
-    call('clang-tidy', f'-p={out}', *ctx.paths)
+    run_clang_tidy = None
+    for var in ('PIGWEED_CIPD_INSTALL_DIR', 'CIPD_INSTALL_DIR'):
+        if var in os.environ:
+            possibility = os.path.join(os.environ[var],
+                                       'share/clang/run-clang-tidy.py')
+            if os.path.isfile(possibility):
+                run_clang_tidy = possibility
+                break
+
+    checks = ','.join(CLANG_TIDY_CHECKS)
+    call(
+        run_clang_tidy,
+        f'-p={ctx.output_directory}',
+        f'-checks={checks}',
+        # TODO(pwbug/45) not sure if this is needed.
+        # f'-extra-arg-before=-warnings-as-errors={checks}',
+        *ctx.paths)
 
 
 CC = (
     pw_presubmit.pragma_once,
-    # TODO(hepler): Enable clang-tidy when it passes.
+    # TODO(pwbug/45): Enable clang-tidy when it passes.
     # clang_tidy,
 )
 
@@ -392,6 +411,11 @@
 
 GENERAL = (source_is_in_build_files, )
 
+BROKEN: Sequence = (
+    # TODO(pwbug/45): Remove clang-tidy from BROKEN when it passes.
+    clang_tidy,
+)  # yapf: disable
+
 #
 # Presubmit check programs
 #
@@ -405,10 +429,13 @@
 )
 
 PROGRAMS: Dict[str, Sequence] = {
+    'broken': BROKEN,
     'full': INIT + GN + CC + PYTHON + CMAKE + BAZEL + CODE_FORMAT + GENERAL,
     'quick': QUICK_PRESUBMIT,
 }
 
+ALL_STEPS = frozenset(itertools.chain(*PROGRAMS.values()))
+
 
 def argument_parser(parser=None) -> argparse.ArgumentParser:
     """Create argument parser."""
@@ -443,13 +470,13 @@
     exclusive.add_argument('-p',
                            '--program',
                            dest='program_name',
-                           choices=PROGRAMS,
+                           choices=[x for x in PROGRAMS if x != 'broken'],
                            default='full',
                            help='Which presubmit program to run')
 
     exclusive.add_argument(
         '--step',
-        choices=[x.__name__ for x in PROGRAMS['full']],
+        choices=sorted(x.__name__ for x in itertools.chain(ALL_STEPS)),
         action='append',
         help='Provide explicit steps instead of running a predefined program.',
     )
@@ -489,7 +516,7 @@
 
     program = PROGRAMS[program_name]
     if step:
-        program = [x for x in PROGRAMS['full'] if x.__name__ in step]
+        program = [x for x in ALL_STEPS if x.__name__ in step]
 
     if pw_presubmit.run_presubmit(program,
                                   repository=repository,