Call clang-tidy based on WITH_TIDY* variables.

* Call clang-tidy before every C/C++ compilation if
  (1) clang-tidy is found at $(PATH_TO_CLANG_TIDY)
  (2) $(my_clang) is true
  (3) LOCAL_TIDY is 1 or true, or
      LOCAL_TIDY is undefined and WITH_TIDY is 1 or true.
* clang-tidy is called with -checks=$(my_tidy_checks),
  which has default '-*,google*,-google-readability*'
  and can be overwritten by WITH_TIDY_CHECKS.
* LOCAL_TIDY_CHECKS is appended to $(my_tidy_checks)
* Extra flags are passed to clang-tidy through
  WITH_TIDY_FLAGS or LOCAL_TIDY_FLAGS.
* To quickly find and fix clang-tidy warnings, set $(WITH_TIDY_ONLY)
  to 1 or true to skip compilation of C/C++ files.
* Add a PHONY tidy_only target,
  which includes all $(cpp_objects) and $(c_objects).
* The 'mm' and 'mmm' functions are changed to call make with
  the 'tidy_only' target when WITH_TIDY_ONLY is true or 1.
  In that case, only clang-tidy is called for C and C++ files.

Bug: http://b/27779618
Change-Id: I8adcfff217d68af49849b79aacee7d7654cafb1b
diff --git a/core/binary.mk b/core/binary.mk
index b25bf08..c017e70 100644
--- a/core/binary.mk
+++ b/core/binary.mk
@@ -1374,6 +1374,45 @@
     my_cflags += -DANDROID_STRICT
 endif
 
+# Disable clang-tidy if it is not found.
+ifeq ($(PATH_TO_CLANG_TIDY),)
+  my_tidy_enabled := false
+else
+  # If LOCAL_TIDY is not defined, use global WITH_TIDY
+  my_tidy_enabled := $(LOCAL_TIDY)
+  ifeq ($(my_tidy_enabled),)
+    my_tidy_enabled := $(WITH_TIDY)
+  endif
+endif
+
+# my_tidy_checks is empty if clang-tidy is disabled.
+my_tidy_checks :=
+my_tidy_flags :=
+ifneq (,$(filter 1 true,$(my_tidy_enabled)))
+  ifneq ($(my_clang),true)
+    # Disable clang-tidy if clang is disabled.
+    my_tidy_enabled := false
+  else
+    tidy_only: $(cpp_objects) $(c_objects)
+    # Set up global default checks
+    my_tidy_checks := $(WITH_TIDY_CHECKS)
+    ifeq ($(my_tidy_checks),)
+      # AOSP source did not follow Google readability rules.
+      my_tidy_checks := -*,google*,-google-readability*
+    endif
+    # Append local clang-tidy checks.
+    ifneq ($(LOCAL_TIDY_CHECKS),)
+      my_tidy_checks := $(my_tidy_checks),$(LOCAL_TIDY_CHECKS)
+    endif
+    # Set up global default clang-tidy flags, which is none.
+    my_tidy_flags := $(WITH_TIDY_FLAGS)
+    # Use local clang-tidy flags if specified.
+    ifneq ($(LOCAL_TIDY_FLAGS),)
+      my_tidy_flags := $(LOCAL_TIDY_FLAGS)
+    endif
+  endif
+endif
+
 $(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_YACCFLAGS := $(LOCAL_YACCFLAGS)
 $(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_ASFLAGS := $(my_asflags)
 $(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_CONLYFLAGS := $(my_conlyflags)
@@ -1387,6 +1426,8 @@
 $(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_IMPORT_INCLUDES := $(import_includes)
 $(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_LDFLAGS := $(my_ldflags)
 $(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_LDLIBS := $(my_ldlibs)
+$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_TIDY_CHECKS := $(my_tidy_checks)
+$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_TIDY_FLAGS := $(my_tidy_flags)
 
 # this is really the way to get the files onto the command line instead
 # of using $^, because then LOCAL_ADDITIONAL_DEPENDENCIES doesn't work
diff --git a/core/clear_vars.mk b/core/clear_vars.mk
index c3d390f..12f09fa 100644
--- a/core/clear_vars.mk
+++ b/core/clear_vars.mk
@@ -165,6 +165,9 @@
 LOCAL_RMTYPEDEFS:=
 LOCAL_NO_SYNTAX_CHECK:=
 LOCAL_NO_STATIC_ANALYZER:=
+LOCAL_TIDY:=
+LOCAL_TIDY_CHECKS:=
+LOCAL_TIDY_FLAGS:=
 LOCAL_32_BIT_ONLY:= # '',true
 LOCAL_MULTILIB:=
 LOCAL_MODULE_TARGET_ARCH:=
diff --git a/core/config.mk b/core/config.mk
index e63a135..eae66d3 100644
--- a/core/config.mk
+++ b/core/config.mk
@@ -348,6 +348,20 @@
 # define clang/llvm versions and base directory.
 include $(BUILD_SYSTEM)/clang/versions.mk
 
+# Unset WITH_TIDY_ONLY if global WITH_TIDY_ONLY is not true nor 1.
+ifeq (,$(filter 1 true,$(WITH_TIDY_ONLY)))
+  WITH_TIDY_ONLY :=
+endif
+
+PATH_TO_CLANG_TIDY := \
+    $(LLVM_PREBUILTS_BASE)/$(BUILD_OS)-x86/$(LLVM_PREBUILTS_VERSION)/bin/clang-tidy
+ifeq ($(wildcard $(PATH_TO_CLANG_TIDY)),)
+  ifneq (,$(filter 1 true,$(WITH_TIDY)))
+    $(warning *** Disable WITH_TIDY because $(PATH_TO_CLANG_TIDY) does not exist)
+  endif
+  PATH_TO_CLANG_TIDY :=
+endif
+
 # Disable WITH_STATIC_ANALYZER and WITH_SYNTAX_CHECK if tool can't be found
 SYNTAX_TOOLS_PREFIX := \
     $(LLVM_PREBUILTS_BASE)/$(BUILD_OS)-x86/$(LLVM_PREBUILTS_VERSION)/tools/scan-build/libexec
diff --git a/core/definitions.mk b/core/definitions.mk
index 9495bc8..8fd7ec9 100644
--- a/core/definitions.mk
+++ b/core/definitions.mk
@@ -1147,10 +1147,7 @@
 ## Commands for running gcc to compile a C++ file
 ###########################################################
 
-define transform-cpp-to-o
-@echo "target $(PRIVATE_ARM_MODE) C++: $(PRIVATE_MODULE) <= $<"
-@mkdir -p $(dir $@)
-$(hide) $(RELATIVE_PWD) $(PRIVATE_CXX) \
+define transform-cpp-to-o-compiler-args
 	$(addprefix -I , $(PRIVATE_C_INCLUDES)) \
 	$$(cat $(PRIVATE_IMPORT_INCLUDES)) \
 	$(addprefix -isystem ,\
@@ -1169,20 +1166,40 @@
 	$(PRIVATE_CPPFLAGS) \
 	$(PRIVATE_DEBUG_CFLAGS) \
 	$(PRIVATE_CFLAGS_NO_OVERRIDE) \
-	$(PRIVATE_CPPFLAGS_NO_OVERRIDE) \
-	-MD -MF $(patsubst %.o,%.d,$@) -o $@ $<
-$(transform-d-to-p)
+	$(PRIVATE_CPPFLAGS_NO_OVERRIDE)
 endef
 
+define clang-tidy-cpp
+$(hide) $(PATH_TO_CLANG_TIDY) $(PRIVATE_TIDY_FLAGS) \
+  -checks=$(PRIVATE_TIDY_CHECKS) \
+  $< -- $(transform-cpp-to-o-compiler-args)
+endef
+
+ifneq (,$(filter 1 true,$(WITH_TIDY_ONLY)))
+define transform-cpp-to-o
+$(if $(PRIVATE_TIDY_CHECKS),
+  @echo "target tidy $(PRIVATE_ARM_MODE) C++: $<"
+  $(clang-tidy-cpp))
+endef
+else
+define transform-cpp-to-o
+@echo "target $(PRIVATE_ARM_MODE) C++: $(PRIVATE_MODULE) <= $<"
+@mkdir -p $(dir $@)
+$(if $(PRIVATE_TIDY_CHECKS),$(clang-tidy-cpp))
+$(hide) $(RELATIVE_PWD) $(PRIVATE_CXX) \
+  $(transform-cpp-to-o-compiler-args) \
+  -MD -MF $(patsubst %.o,%.d,$@) -o $@ $<
+$(hide) $(transform-d-to-p)
+endef
+endif
+
 
 ###########################################################
 ## Commands for running gcc to compile a C file
 ###########################################################
 
 # $(1): extra flags
-define transform-c-or-s-to-o-no-deps
-@mkdir -p $(dir $@)
-$(hide) $(RELATIVE_PWD) $(PRIVATE_CC) \
+define transform-c-or-s-to-o-compiler-args
 	$(addprefix -I , $(PRIVATE_C_INCLUDES)) \
 	$$(cat $(PRIVATE_IMPORT_INCLUDES)) \
 	$(addprefix -isystem ,\
@@ -1196,27 +1213,47 @@
 	    $(PRIVATE_TARGET_GLOBAL_CONLYFLAGS) \
 	    $(PRIVATE_ARM_CFLAGS) \
 	 ) \
-	 $(1) \
-	-MD -MF $(patsubst %.o,%.d,$@) -o $@ $<
+	 $(1)
 endef
 
-define transform-c-to-o-no-deps
-@echo "target $(PRIVATE_ARM_MODE) C: $(PRIVATE_MODULE) <= $<"
-$(call transform-c-or-s-to-o-no-deps, \
-    $(PRIVATE_CFLAGS) \
-    $(PRIVATE_CONLYFLAGS) \
-    $(PRIVATE_DEBUG_CFLAGS) \
-    $(PRIVATE_CFLAGS_NO_OVERRIDE))
+define transform-c-to-o-compiler-args
+$(call transform-c-or-s-to-o-compiler-args, \
+  $(PRIVATE_CFLAGS) \
+  $(PRIVATE_CONLYFLAGS) \
+  $(PRIVATE_DEBUG_CFLAGS) \
+  $(PRIVATE_CFLAGS_NO_OVERRIDE))
 endef
 
+define clang-tidy-c
+$(hide) $(PATH_TO_CLANG_TIDY) $(PRIVATE_TIDY_FLAGS) \
+  -checks=$(PRIVATE_TIDY_CHECKS) \
+  $< -- $(transform-c-to-o-compiler-args)
+endef
+
+ifneq (,$(filter 1 true,$(WITH_TIDY_ONLY)))
+define transform-c-to-o
+$(if $(PRIVATE_TIDY_CHECKS),
+  @echo "target tidy $(PRIVATE_ARM_MODE) C: $<"
+  $(clang-tidy-c))
+endef
+else
+define transform-c-to-o
+@echo "target $(PRIVATE_ARM_MODE) C: $(PRIVATE_MODULE) <= $<"
+@mkdir -p $(dir $@)
+$(if $(PRIVATE_TIDY_CHECKS),$(clang-tidy-c))
+$(hide) $(RELATIVE_PWD) $(PRIVATE_CC) \
+  $(transform-c-to-o-compiler-args) \
+  -MD -MF $(patsubst %.o,%.d,$@) -o $@ $<
+$(hide) $(transform-d-to-p)
+endef
+endif
+
 define transform-s-to-o-no-deps
 @echo "target asm: $(PRIVATE_MODULE) <= $<"
-$(call transform-c-or-s-to-o-no-deps, $(PRIVATE_ASFLAGS))
-endef
-
-define transform-c-to-o
-$(transform-c-to-o-no-deps)
-$(transform-d-to-p)
+@mkdir -p $(dir $@)
+$(RELATIVE_PWD) $(PRIVATE_CC) \
+  $(call transform-c-or-s-to-o-compiler-args, $(PRIVATE_ASFLAGS)) \
+  -MD -MF $(patsubst %.o,%.d,$@) -o $@ $<
 endef
 
 define transform-s-to-o
@@ -1254,10 +1291,7 @@
 ## Commands for running gcc to compile a host C++ file
 ###########################################################
 
-define transform-host-cpp-to-o
-@echo "$($(PRIVATE_PREFIX)DISPLAY) C++: $(PRIVATE_MODULE) <= $<"
-@mkdir -p $(dir $@)
-$(hide) $(RELATIVE_PWD) $(PRIVATE_CXX) \
+define transform-host-cpp-to-o-compiler-args
 	$(addprefix -I , $(PRIVATE_C_INCLUDES)) \
 	$$(cat $(PRIVATE_IMPORT_INCLUDES)) \
 	$(addprefix -isystem ,\
@@ -1274,20 +1308,39 @@
 	$(PRIVATE_CPPFLAGS) \
 	$(PRIVATE_DEBUG_CFLAGS) \
 	$(PRIVATE_CFLAGS_NO_OVERRIDE) \
-	$(PRIVATE_CPPFLAGS_NO_OVERRIDE) \
-	-MD -MF $(patsubst %.o,%.d,$@) -o $@ $<
-$(transform-d-to-p)
+	$(PRIVATE_CPPFLAGS_NO_OVERRIDE)
 endef
 
+define clang-tidy-host-cpp
+$(hide) $(PATH_TO_CLANG_TIDY) $(PRIVATE_TIDY_FLAGS) \
+  -checks=$(PRIVATE_TIDY_CHECKS) \
+  $< -- $(transform-host-cpp-to-o-compiler-args)
+endef
+
+ifneq (,$(filter 1 true,$(WITH_TIDY_ONLY)))
+define transform-host-cpp-to-o
+$(if $(PRIVATE_TIDY_CHECKS),
+  @echo "tidy $($(PRIVATE_PREFIX)DISPLAY) C++: $<"
+  $(clang-tidy-host-cpp))
+endef
+else
+define transform-host-cpp-to-o
+@echo "$($(PRIVATE_PREFIX)DISPLAY) C++: $(PRIVATE_MODULE) <= $<"
+@mkdir -p $(dir $@)
+$(if $(PRIVATE_TIDY_CHECKS),$(clang-tidy-host-cpp))
+$(hide) $(RELATIVE_PWD) $(PRIVATE_CXX) \
+  $(transform-host-cpp-to-o-compiler-args) \
+  -MD -MF $(patsubst %.o,%.d,$@) -o $@ $<
+$(hide) $(transform-d-to-p)
+endef
+endif
+
 
 ###########################################################
 ## Commands for running gcc to compile a host C file
 ###########################################################
 
-# $(1): extra flags
-define transform-host-c-or-s-to-o-no-deps
-@mkdir -p $(dir $@)
-$(hide) $(RELATIVE_PWD) $(PRIVATE_CC) \
+define transform-host-c-or-s-to-o-common-args
 	$(addprefix -I , $(PRIVATE_C_INCLUDES)) \
 	$$(cat $(PRIVATE_IMPORT_INCLUDES)) \
 	$(addprefix -isystem ,\
@@ -1299,26 +1352,53 @@
 	$(if $(PRIVATE_NO_DEFAULT_COMPILER_FLAGS),, \
 	    $(PRIVATE_HOST_GLOBAL_CFLAGS) \
 	    $(PRIVATE_HOST_GLOBAL_CONLYFLAGS) \
-	 ) \
-	$(1) \
-	-MD -MF $(patsubst %.o,%.d,$@) -o $@ $<
+	 )
 endef
 
-define transform-host-c-to-o-no-deps
-@echo "$($(PRIVATE_PREFIX)DISPLAY) C: $(PRIVATE_MODULE) <= $<"
-$(call transform-host-c-or-s-to-o-no-deps, $(PRIVATE_CFLAGS) $(PRIVATE_CONLYFLAGS) $(PRIVATE_DEBUG_CFLAGS) $(PRIVATE_CFLAGS_NO_OVERRIDE))
+# $(1): extra flags
+define transform-host-c-or-s-to-o-no-deps
+@mkdir -p $(dir $@)
+$(hide) $(RELATIVE_PWD) $(PRIVATE_CC) \
+  $(transform-host-c-or-s-to-o-common-args) \
+  $(1) \
+  -MD -MF $(patsubst %.o,%.d,$@) -o $@ $<
 endef
 
+define transform-host-c-to-o-compiler-args
+  $(transform-host-c-or-s-to-o-common-args) \
+  $(PRIVATE_CFLAGS) $(PRIVATE_CONLYFLAGS) \
+  $(PRIVATE_DEBUG_CFLAGS) $(PRIVATE_CFLAGS_NO_OVERRIDE)
+endef
+
+define clang-tidy-host-c
+$(hide) $(PATH_TO_CLANG_TIDY) $(PRIVATE_TIDY_FLAGS) \
+  -checks=$(PRIVATE_TIDY_CHECKS) \
+  $< -- $(transform-host-c-to-o-compiler-args)
+endef
+
+ifneq (,$(filter 1 true,$(WITH_TIDY_ONLY)))
+define transform-host-c-to-o
+$(if $(PRIVATE_TIDY_CHECKS),
+  @echo "tidy $($(PRIVATE_PREFIX)DISPLAY) C: $<"
+  $(clang-tidy-host-c))
+endef
+else
+define transform-host-c-to-o
+@echo "$($(PRIVATE_PREFIX)DISPLAY) C: $(PRIVATE_MODULE) <= $<"
+@mkdir -p $(dir $@)
+$(if $(PRIVATE_TIDY_CHECKS), $(clang-tidy-host-c))
+$(hide) $(RELATIVE_PWD) $(PRIVATE_CC) \
+  $(transform-host-c-to-o-compiler-args) \
+  -MD -MF $(patsubst %.o,%.d,$@) -o $@ $<
+$(hide) $(transform-d-to-p)
+endef
+endif
+
 define transform-host-s-to-o-no-deps
 @echo "$($(PRIVATE_PREFIX)DISPLAY) asm: $(PRIVATE_MODULE) <= $<"
 $(call transform-host-c-or-s-to-o-no-deps, $(PRIVATE_ASFLAGS))
 endef
 
-define transform-host-c-to-o
-$(transform-host-c-to-o-no-deps)
-$(transform-d-to-p)
-endef
-
 define transform-host-s-to-o
 $(transform-host-s-to-o-no-deps)
 $(transform-d-to-p)
diff --git a/core/main.mk b/core/main.mk
index ad1b663..c455320 100644
--- a/core/main.mk
+++ b/core/main.mk
@@ -1126,4 +1126,9 @@
 .PHONY: nothing
 nothing:
 	@echo Successfully read the makefiles.
+
+.PHONY: tidy_only
+tidy_only:
+	@echo Successfully make tidy_only.
+
 endif # KATI
diff --git a/envsetup.sh b/envsetup.sh
index b1aab34..f050a9a 100644
--- a/envsetup.sh
+++ b/envsetup.sh
@@ -794,6 +794,9 @@
               MODULES=all_modules
               ARGS=$@
             fi
+            if [ "1" = "${WITH_TIDY_ONLY}" -o "true" = "${WITH_TIDY_ONLY}" ]; then
+              MODULES=tidy_only
+            fi
             ONE_SHOT_MAKEFILE=$M $DRV make -C $T -f build/core/main.mk $MODULES $ARGS
         fi
     fi
@@ -845,6 +848,9 @@
           ARGS=$GET_INSTALL_PATH
           MODULES=
         fi
+        if [ "1" = "${WITH_TIDY_ONLY}" -o "true" = "${WITH_TIDY_ONLY}" ]; then
+          MODULES=tidy_only
+        fi
         ONE_SHOT_MAKEFILE="$MAKEFILE" $DRV make -C $T -f build/core/main.mk $DASH_ARGS $MODULES $ARGS
     else
         echo "Couldn't locate the top of the tree.  Try setting TOP."