Merge "Update package whitelist to track changes to java.lang.invoke."
diff --git a/core/Makefile b/core/Makefile
index c86c499..7082f5a 100644
--- a/core/Makefile
+++ b/core/Makefile
@@ -400,6 +400,15 @@
 endif
 
 # -----------------------------------------------------------------
+# build system stats
+BUILD_SYSTEM_STATS := $(PRODUCT_OUT)/build_system_stats.txt
+$(BUILD_SYSTEM_STATS):
+	@rm -f $@
+	@$(foreach s,$(STATS.MODULE_TYPE),echo "modules_type_make,$(s),$(words $(STATS.MODULE_TYPE.$(s)))" >>$@;)
+	@$(foreach s,$(STATS.SOONG_MODULE_TYPE),echo "modules_type_soong,$(s),$(STATS.SOONG_MODULE_TYPE.$(s))" >>$@;)
+$(call dist-for-goals,droidcore,$(BUILD_SYSTEM_STATS))
+
+# -----------------------------------------------------------------
 
 # The dev key is used to sign this package, and as the key required
 # for future OTA packages installed by this system.  Actual product
@@ -1612,10 +1621,9 @@
   $(HOST_LIBRARY_PATH)/libconscrypt_openjdk_jni$(HOST_SHLIB_SUFFIX) \
   $(HOST_LIBRARY_PATH)/libbrillo$(HOST_SHLIB_SUFFIX) \
   $(HOST_LIBRARY_PATH)/libbrillo-stream$(HOST_SHLIB_SUFFIX) \
-  $(HOST_LIBRARY_PATH)/libbrillo-http$(HOST_SHLIB_SUFFIX) \
   $(HOST_LIBRARY_PATH)/libchrome$(HOST_SHLIB_SUFFIX) \
   $(HOST_LIBRARY_PATH)/libcurl-host$(HOST_SHLIB_SUFFIX) \
-  $(HOST_LIBRARY_PATH)/libevent$(HOST_SHLIB_SUFFIX) \
+  $(HOST_LIBRARY_PATH)/libevent-host$(HOST_SHLIB_SUFFIX) \
   $(HOST_LIBRARY_PATH)/libprotobuf-cpp-lite$(HOST_SHLIB_SUFFIX) \
   $(HOST_LIBRARY_PATH)/libssl-host$(HOST_SHLIB_SUFFIX) \
   $(HOST_LIBRARY_PATH)/libz-host$(HOST_SHLIB_SUFFIX) \
diff --git a/core/base_rules.mk b/core/base_rules.mk
index 8a1c5ee..1daf229 100644
--- a/core/base_rules.mk
+++ b/core/base_rules.mk
@@ -14,6 +14,9 @@
 # limitations under the License.
 #
 
+# Catch users that directly include base_rules.mk
+$(call record-module-type,base_rules)
+
 # Users can define base-rules-hook in their buildspec.mk to perform
 # arbitrary operations as each module is included.
 ifdef base-rules-hook
@@ -333,21 +336,22 @@
 	$(copy-file-to-new-target)
 	$(PRIVATE_POST_INSTALL_CMD)
 
+ifndef LOCAL_IS_HOST_MODULE
 # Rule to install the module's companion init.rc.
-my_init_rc := $(LOCAL_INIT_RC_$(my_32_64_bit_suffix))
-ifneq ($(my_init_rc),)
-my_init_rc_pairs += $(LOCAL_PATH)/$(my_init_rc):$(TARGET_OUT$(partition_tag)_ETC)/init/$(notdir $(my_init_rc))
-endif
-ifneq ($(LOCAL_INIT_RC),)
-my_init_rc_pairs += $(LOCAL_PATH)/$(LOCAL_INIT_RC):$(TARGET_OUT$(partition_tag)_ETC)/init/$(notdir $(LOCAL_INIT_RC))
-# Make sure we don't define the rule twice in multilib module.
-LOCAL_INIT_RC :=
-endif
-ifneq ($(my_init_rc_pairs),)
-my_init_rc_installed := $(call copy-many-files,$(my_init_rc_pairs))
+my_init_rc := $(LOCAL_INIT_RC_$(my_32_64_bit_suffix)) $(LOCAL_INIT_RC)
+ifneq ($(strip $(my_init_rc)),)
+my_init_rc_pairs := $(foreach rc,$(my_init_rc),$(LOCAL_PATH)/$(rc):$(TARGET_OUT$(partition_tag)_ETC)/init/$(notdir $(rc)))
+my_init_rc_installed := $(foreach rc,$(my_init_rc_pairs),$(call word-colon,2,$(rc)))
+
+# Make sure we only set up the copy rules once, even if another arch variant
+# shares a common LOCAL_INIT_RC.
+my_init_rc_new_pairs := $(filter-out $(ALL_INIT_RC_INSTALLED_PAIRS),$(my_init_rc_pairs))
+my_init_rc_new_installed := $(call copy-many-files,$(my_init_rc_new_pairs))
+ALL_INIT_RC_INSTALLED_PAIRS += $(my_init_rc_new_pairs)
 
 $(my_register_name) : $(my_init_rc_installed)
-endif # my_init_rc_pairs
+endif # my_init_rc
+endif # !LOCAL_IS_HOST_MODULE
 
 # Rule to install the module's companion symlinks
 my_installed_symlinks := $(addprefix $(my_module_path)/,$(LOCAL_MODULE_SYMLINKS) $(LOCAL_MODULE_SYMLINKS_$(my_32_64_bit_suffix)))
diff --git a/core/binary.mk b/core/binary.mk
index e78f6a5..4554d4d 100644
--- a/core/binary.mk
+++ b/core/binary.mk
@@ -70,6 +70,9 @@
     $(error $(LOCAL_PATH): LOCAL_SDK_VERSION cannot be used in host module)
   endif
 
+  # Make sure we've built the NDK.
+  my_additional_dependencies += $(SOONG_OUT_DIR)/ndk.timestamp
+
   # mips32r6 is not supported by the NDK. No released NDK contains these
   # libraries, but the r10 in prebuilts/ndk had a local hack to add them :(
   #
@@ -81,9 +84,31 @@
     endif
   endif
 
-  my_ndk_source_root := $(HISTORICAL_NDK_VERSIONS_ROOT)/$(LOCAL_NDK_VERSION)/sources
-  my_ndk_sysroot := $(HISTORICAL_NDK_VERSIONS_ROOT)/$(LOCAL_NDK_VERSION)/platforms/android-$(LOCAL_SDK_VERSION)/arch-$(TARGET_$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH)
-  my_ndk_sysroot_include := $(my_ndk_sysroot)/usr/include
+  my_arch := $(TARGET_$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH)
+  ifneq (,$(filter arm64 mips64 x86_64,$(my_arch)))
+    my_min_sdk_version := 21
+  else
+    my_min_sdk_version := 9
+  endif
+
+  # Historically we've just set up a bunch of symlinks in prebuilts/ndk to map
+  # missing API levels to existing ones where necessary, but we're not doing
+  # that for the generated libraries. Clip the API level to the minimum where
+  # appropriate.
+  my_ndk_api := \
+    $(shell if [ $(LOCAL_SDK_VERSION) -lt $(my_min_sdk_version) ]; then \
+        echo $(my_min_sdk_version); else echo $(LOCAL_SDK_VERSION); fi)
+
+  my_ndk_source_root := \
+      $(HISTORICAL_NDK_VERSIONS_ROOT)/$(LOCAL_NDK_VERSION)/sources
+  my_ndk_sysroot := \
+    $(HISTORICAL_NDK_VERSIONS_ROOT)/$(LOCAL_NDK_VERSION)/platforms/android-$(my_ndk_api)/arch-$(my_arch)
+  my_built_ndk := $(SOONG_OUT_DIR)/ndk
+  my_ndk_triple := $($(LOCAL_2ND_ARCH_VAR_PREFIX)TARGET_NDK_TRIPLE)
+  my_ndk_sysroot_include := \
+      $(my_built_ndk)/sysroot/usr/include \
+      $(my_built_ndk)/sysroot/usr/include/$(my_ndk_triple) \
+      $(my_ndk_sysroot)/usr/include \
 
   # x86_64 and and mips64 are both multilib toolchains, so their libraries are
   # installed in /usr/lib64. Aarch64, on the other hand, is not a multilib
@@ -92,14 +117,19 @@
   # Mips32r6 is yet another variation, with libraries installed in libr6.
   #
   # For the rest, the libraries are installed simply to /usr/lib.
-  ifneq (,$(filter x86_64 mips64,$(TARGET_$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH)))
-    my_ndk_sysroot_lib := $(my_ndk_sysroot)/usr/lib64
+  ifneq (,$(filter x86_64 mips64,$(my_arch)))
+    my_ndk_libdir_name := lib64
   else ifeq (mips32r6,$(TARGET_$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH_VARIANT))
-    my_ndk_sysroot_lib := $(my_ndk_sysroot)/usr/libr6
+    my_ndk_libdir_name := libr6
   else
-    my_ndk_sysroot_lib := $(my_ndk_sysroot)/usr/lib
+    my_ndk_libdir_name := lib
   endif
 
+  my_ndk_platform_dir := \
+      $(my_built_ndk)/platforms/android-$(my_ndk_api)/arch-$(my_arch)
+  my_built_ndk_libs := $(my_ndk_platform_dir)/usr/$(my_ndk_libdir_name)
+  my_ndk_sysroot_lib := $(my_ndk_sysroot)/usr/$(my_ndk_libdir_name)
+
   # The bionic linker now has support for packed relocations and gnu style
   # hashes (which are much faster!), but shipping to older devices requires
   # the old style hash. Fortunately, we can build with both and it'll work
@@ -180,6 +210,11 @@
   endif
   endif
   endif
+
+  my_generated_ndk_shared_libraries := \
+      $(filter $(NDK_MIGRATED_LIBS),$(my_system_shared_libraries))
+  my_system_shared_libraries := \
+      $(filter-out $(NDK_MIGRATED_LIBS),$(my_system_shared_libraries))
 endif
 
 # MinGW spits out warnings about -fPIC even for -fpie?!) being ignored because
@@ -337,11 +372,16 @@
 ###########################################################
 ifndef LOCAL_IS_HOST_MODULE
 ifdef LOCAL_SDK_VERSION
-my_target_global_c_includes := $(my_ndk_stl_include_path) $(my_ndk_sysroot_include)
+my_target_global_c_includes :=
+my_target_global_c_system_includes := $(my_ndk_stl_include_path) $(my_ndk_sysroot_include)
 my_target_global_cppflags := $(my_ndk_stl_cppflags)
 else
-my_target_global_c_includes := $($(LOCAL_2ND_ARCH_VAR_PREFIX)TARGET_PROJECT_INCLUDES) \
+my_target_global_c_includes := $(SRC_HEADERS) \
+    $($(LOCAL_2ND_ARCH_VAR_PREFIX)TARGET_PROJECT_INCLUDES) \
     $($(LOCAL_2ND_ARCH_VAR_PREFIX)TARGET_C_INCLUDES)
+my_target_global_c_system_includes := $(SRC_SYSTEM_HEADERS) \
+    $($(LOCAL_2ND_ARCH_VAR_PREFIX)TARGET_PROJECT_SYSTEM_INCLUDES) \
+    $($(LOCAL_2ND_ARCH_VAR_PREFIX)TARGET_C_SYSTEM_INCLUDES)
 my_target_global_cppflags :=
 endif # LOCAL_SDK_VERSION
 
@@ -358,6 +398,7 @@
 endif # my_clang
 
 $(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_GLOBAL_C_INCLUDES := $(my_target_global_c_includes)
+$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_GLOBAL_C_SYSTEM_INCLUDES := $(my_target_global_c_system_includes)
 $(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_TARGET_GLOBAL_CFLAGS := $(my_target_global_cflags)
 $(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_TARGET_GLOBAL_CONLYFLAGS := $(my_target_global_conlyflags)
 $(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_TARGET_GLOBAL_CPPFLAGS := $(my_target_global_cppflags)
@@ -365,8 +406,12 @@
 
 else # LOCAL_IS_HOST_MODULE
 
-my_host_global_c_includes := $($(LOCAL_2ND_ARCH_VAR_PREFIX)$(my_prefix)PROJECT_INCLUDES) \
+my_host_global_c_includes := $(SRC_HEADERS) \
+    $($(LOCAL_2ND_ARCH_VAR_PREFIX)$(my_prefix)PROJECT_INCLUDES) \
     $($(LOCAL_2ND_ARCH_VAR_PREFIX)$(my_prefix)C_INCLUDES)
+my_host_global_c_system_includes := $(SRC_SYSTEM_HEADERS) \
+    $($(LOCAL_2ND_ARCH_VAR_PREFIX)$(my_prefix)PROJECT_SYSTEM_INCLUDES) \
+    $($(LOCAL_2ND_ARCH_VAR_PREFIX)$(my_prefix)C_SYSTEM_INCLUDES)
 
 ifeq ($(my_clang),true)
 my_host_global_cflags := $($(LOCAL_2ND_ARCH_VAR_PREFIX)CLANG_$(my_prefix)GLOBAL_CFLAGS)
@@ -381,6 +426,7 @@
 endif # my_clang
 
 $(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_GLOBAL_C_INCLUDES := $(my_host_global_c_includes)
+$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_GLOBAL_C_SYSTEM_INCLUDES := $(my_host_global_c_system_includes)
 $(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_HOST_GLOBAL_CFLAGS := $(my_host_global_cflags)
 $(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_HOST_GLOBAL_CONLYFLAGS := $(my_host_global_conlyflags)
 $(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_HOST_GLOBAL_CPPFLAGS := $(my_host_global_cppflags)
@@ -1354,7 +1400,14 @@
     $(addprefix $(my_ndk_sysroot_lib)/, \
         $(addsuffix $(so_suffix), $(my_system_shared_libraries)))
 
-built_shared_libraries += $(my_system_shared_libraries_fullpath)
+my_built_ndk_shared_libraries_fullpath := \
+    $(addprefix $(my_built_ndk_libs)/,\
+        $(addsuffix $(so_suffix),$(my_generated_ndk_shared_libraries)))
+
+built_shared_libraries += \
+    $(my_built_ndk_shared_libraries_fullpath) \
+    $(my_system_shared_libraries_fullpath) \
+
 else
 built_shared_libraries := \
     $(addprefix $($(LOCAL_2ND_ARCH_VAR_PREFIX)$(my_prefix)OUT_INTERMEDIATE_LIBRARIES)/, \
diff --git a/core/clang/tidy.mk b/core/clang/tidy.mk
index 35871f0..d50b43d 100644
--- a/core/clang/tidy.mk
+++ b/core/clang/tidy.mk
@@ -15,10 +15,12 @@
 #
 
 # Most Android source files are not clang-tidy clean yet.
-# Global tidy checks include only google* and misc-macro-parentheses,
-# but not google-readability* or google-runtime-references.
+# Global tidy checks include only google*, performance*,
+# and misc-macro-parentheses, but not google-readability*
+# or google-runtime-references.
 DEFAULT_GLOBAL_TIDY_CHECKS := \
-  -*,google*,-google-readability*,-google-runtime-references,misc-macro-parentheses
+  -*,google*,performance*,misc-macro-parentheses \
+  ,-google-readability*,-google-runtime-references
 
 # Disable style rules usually not followed by external projects.
 # Every word in DEFAULT_LOCAL_TIDY_CHECKS list has the following format:
diff --git a/core/clear_vars.mk b/core/clear_vars.mk
index 31b337a..6121c45 100644
--- a/core/clear_vars.mk
+++ b/core/clear_vars.mk
@@ -199,7 +199,9 @@
 # to avoid installing the presigned apks with classes.dex unstripped.
 LOCAL_REPLACE_PREBUILT_APK_INSTALLED:=
 LOCAL_NDK_VERSION:=current
+LOCAL_COPY_TO_INTERMEDIATE_LIBRARIES:=
 LOCAL_LOGTAGS_FILES:=
+LOCAL_RECORDED_MODULE_TYPE:=
 
 # arch specific variables
 LOCAL_SRC_FILES_$(TARGET_ARCH):=
diff --git a/core/config.mk b/core/config.mk
index 0699a14..594c273 100644
--- a/core/config.mk
+++ b/core/config.mk
@@ -39,17 +39,17 @@
 # TODO: Enforce some kind of layering; only add include paths
 #       when a module links against a particular library.
 # TODO: See if we can remove most of these from the global list.
-SRC_HEADERS := \
-	$(TOPDIR)system/core/include \
-	$(TOPDIR)system/media/audio/include \
-	$(TOPDIR)hardware/libhardware/include \
-	$(TOPDIR)hardware/libhardware_legacy/include \
-	$(TOPDIR)hardware/ril/include \
-	$(TOPDIR)libnativehelper/include \
-	$(TOPDIR)frameworks/native/include \
-	$(TOPDIR)frameworks/native/opengl/include \
-	$(TOPDIR)frameworks/av/include \
-	$(TOPDIR)frameworks/base/include
+SRC_SYSTEM_HEADERS := \
+	$(wildcard system/core/include) \
+	$(wildcard system/media/audio/include) \
+	$(wildcard hardware/libhardware/include) \
+	$(wildcard hardware/libhardware_legacy/include) \
+	$(wildcard hardware/ril/include) \
+	$(wildcard libnativehelper/include) \
+	$(wildcard frameworks/native/include) \
+	$(wildcard frameworks/native/opengl/include) \
+	$(wildcard frameworks/av/include) \
+	$(wildcard frameworks/base/include)
 SRC_TARGET_DIR := $(TOPDIR)build/target
 SRC_API_DIR := $(TOPDIR)prebuilts/sdk/api
 SRC_SYSTEM_API_DIR := $(TOPDIR)prebuilts/sdk/system-api
@@ -665,28 +665,34 @@
 HOST_GLOBAL_LD_DIRS := -L$(HOST_OUT_INTERMEDIATE_LIBRARIES)
 TARGET_GLOBAL_LD_DIRS := -L$(TARGET_OUT_INTERMEDIATE_LIBRARIES)
 
-HOST_PROJECT_INCLUDES:= $(SRC_HEADERS) $(HOST_OUT_HEADERS)
-TARGET_PROJECT_INCLUDES:= $(SRC_HEADERS) $(TARGET_OUT_HEADERS) \
+HOST_PROJECT_INCLUDES :=
+HOST_PROJECT_SYSTEM_INCLUDES := $(HOST_OUT_HEADERS)
+TARGET_PROJECT_INCLUDES :=
+TARGET_PROJECT_SYSTEM_INCLUDES := $(TARGET_OUT_HEADERS) \
 		$(TARGET_DEVICE_KERNEL_HEADERS) $(TARGET_BOARD_KERNEL_HEADERS) \
 		$(TARGET_PRODUCT_KERNEL_HEADERS)
 
 ifdef TARGET_2ND_ARCH
 $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_GLOBAL_LD_DIRS := -L$($(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_INTERMEDIATE_LIBRARIES)
 $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_PROJECT_INCLUDES := $(TARGET_PROJECT_INCLUDES)
+$(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_PROJECT_SYSTEM_INCLUDES := $(TARGET_PROJECT_SYSTEM_INCLUDES)
 endif
 
 ifdef HOST_2ND_ARCH
 $(HOST_2ND_ARCH_VAR_PREFIX)HOST_GLOBAL_LD_DIRS := -L$($(HOST_2ND_ARCH_VAR_PREFIX)HOST_OUT_INTERMEDIATE_LIBRARIES)
 $(HOST_2ND_ARCH_VAR_PREFIX)HOST_PROJECT_INCLUDES := $(HOST_PROJECT_INCLUDES)
+$(HOST_2ND_ARCH_VAR_PREFIX)HOST_PROJECT_SYSTEM_INCLUDES := $(HOST_PROJECT_SYSTEM_INCLUDES)
 endif
 
 ifdef HOST_CROSS_OS
 HOST_CROSS_GLOBAL_LD_DIRS := -L$(HOST_CROSS_OUT_INTERMEDIATE_LIBRARIES)
-HOST_CROSS_PROJECT_INCLUDES:= $(SRC_HEADERS) $(HOST_CROSS_OUT_HEADERS)
+HOST_CROSS_PROJECT_INCLUDES :=
+HOST_CROSS_PROJECT_SYSTEM_INCLUDES := $(HOST_CROSS_OUT_HEADERS)
 
 ifdef HOST_CROSS_2ND_ARCH
 $(HOST_CROSS_2ND_ARCH_VAR_PREFIX)HOST_CROSS_GLOBAL_LD_DIRS := -L$($(HOST_CROSS_2ND_ARCH_VAR_PREFIX)HOST_CROSS_OUT_INTERMEDIATE_LIBRARIES)
 $(HOST_CROSS_2ND_ARCH_VAR_PREFIX)HOST_CROSS_PROJECT_INCLUDES:= $(HOST_CROSS_PROJECT_INCLUDES)
+$(HOST_CROSS_2ND_ARCH_VAR_PREFIX)HOST_CROSS_PROJECT_SYSTEM_INCLUDES:= $(HOST_CROSS_PROJECT_SYSTEM_INCLUDES)
 endif
 endif
 
diff --git a/core/copy_headers.mk b/core/copy_headers.mk
index 7d5a5d9..417a76c 100644
--- a/core/copy_headers.mk
+++ b/core/copy_headers.mk
@@ -1,6 +1,7 @@
 ###########################################################
 ## Copy headers to the install tree
 ###########################################################
+$(call record-module-type,COPY_HEADERS)
 ifneq ($(strip $(LOCAL_IS_HOST_MODULE)),)
   my_prefix := HOST_
 else
diff --git a/core/definitions.mk b/core/definitions.mk
index 6bd9cf0..8ef67f1 100644
--- a/core/definitions.mk
+++ b/core/definitions.mk
@@ -95,6 +95,9 @@
 HOST_DISPLAY := host
 HOST_CROSS_DISPLAY := host cross
 
+# All installed initrc files
+ALL_INIT_RC_INSTALLED_PAIRS :=
+
 ###########################################################
 ## Debugging; prints a variable list to stdout
 ###########################################################
@@ -1156,9 +1159,12 @@
 $(addprefix -I , $(PRIVATE_C_INCLUDES)) \
 $$(cat $(PRIVATE_IMPORT_INCLUDES))\
 $(if $(PRIVATE_NO_DEFAULT_COMPILER_FLAGS),,\
+    $(addprefix -I ,\
+        $(filter-out $(PRIVATE_C_INCLUDES), \
+            $(PRIVATE_GLOBAL_C_INCLUDES))) \
     $(addprefix -isystem ,\
         $(filter-out $(PRIVATE_C_INCLUDES), \
-            $(PRIVATE_GLOBAL_C_INCLUDES))))
+            $(PRIVATE_GLOBAL_C_SYSTEM_INCLUDES))))
 endef
 
 ###########################################################
@@ -1689,7 +1695,7 @@
 	$(PRIVATE_TARGET_LIBGCC) \
 	$(PRIVATE_TARGET_GLOBAL_LDFLAGS) \
 	$(PRIVATE_LDFLAGS) \
-	$(call normalize-target-libraries,$(PRIVATE_ALL_SHARED_LIBRARIES)) \
+	$(PRIVATE_ALL_SHARED_LIBRARIES) \
 	-o $@ \
 	$(PRIVATE_TARGET_CRTEND_SO_O) \
 	$(PRIVATE_LDLIBS)
@@ -1777,7 +1783,7 @@
 	$(PRIVATE_TARGET_LIBGCC) \
 	$(PRIVATE_TARGET_GLOBAL_LDFLAGS) \
 	$(PRIVATE_LDFLAGS) \
-	$(call normalize-target-libraries,$(PRIVATE_ALL_SHARED_LIBRARIES)) \
+	$(PRIVATE_ALL_SHARED_LIBRARIES) \
 	-o $@ \
 	$(PRIVATE_TARGET_CRTEND_O) \
 	$(PRIVATE_LDLIBS)
@@ -2837,6 +2843,48 @@
   $(if $(filter $(TARGET_2ND_ARCH),$(1)),$(TARGET_2ND_ARCH),$(if $(1),none))))
 endef
 
+# ###############################################################
+# Set up statistics gathering
+# ###############################################################
+STATS.MODULE_TYPE := \
+  HOST_STATIC_LIBRARY \
+  HOST_SHARED_LIBRARY \
+  STATIC_LIBRARY \
+  SHARED_LIBRARY \
+  EXECUTABLE \
+  HOST_EXECUTABLE \
+  PACKAGE \
+  PHONY_PACKAGE \
+  HOST_PREBUILT \
+  PREBUILT \
+  MULTI_PREBUILT \
+  JAVA_LIBRARY \
+  STATIC_JAVA_LIBRARY \
+  HOST_JAVA_LIBRARY \
+  DROIDDOC \
+  COPY_HEADERS \
+  NATIVE_TEST \
+  NATIVE_BENCHMARK \
+  HOST_NATIVE_TEST \
+  FUZZ_TEST \
+  HOST_FUZZ_TEST \
+  STATIC_TEST_LIBRARY \
+  HOST_STATIC_TEST_LIBRARY \
+  NOTICE_FILE \
+  HOST_DALVIK_JAVA_LIBRARY \
+  HOST_DALVIK_STATIC_JAVA_LIBRARY \
+  base_rules
+
+$(foreach $(s),$(STATS.MODULE_TYPE),$(eval STATS.MODULE_TYPE.$(s) :=))
+define record-module-type
+$(strip $(if $(LOCAL_RECORDED_MODULE_TYPE),,
+  $(if $(filter-out $(SOONG_ANDROID_MK),$(LOCAL_MODULE_MAKEFILE)),
+    $(if $(filter $(1),$(STATS.MODULE_TYPE)),
+      $(eval LOCAL_RECORDED_MODULE_TYPE := true)
+        $(eval STATS.MODULE_TYPE.$(1) += 1),
+      $(error Invalid module type: $(1))))))
+endef
+
 ###########################################################
 ## Other includes
 ###########################################################
diff --git a/core/droiddoc.mk b/core/droiddoc.mk
index 0f18a58..06506db 100644
--- a/core/droiddoc.mk
+++ b/core/droiddoc.mk
@@ -14,6 +14,7 @@
 # limitations under the License.
 #
 
+$(call record-module-type,DROIDDOC)
 ##
 ##
 ## Common to both droiddoc and javadoc
diff --git a/core/executable.mk b/core/executable.mk
index 8652077..f1b2462 100644
--- a/core/executable.mk
+++ b/core/executable.mk
@@ -16,6 +16,7 @@
 endif
 
 ifneq (true,$(my_skip_this_target))
+$(call record-module-type,EXECUTABLE)
 
 ifeq ($(TARGET_TRANSLATE_2ND_ARCH),true)
 # If a native test explicity specifies to build only for the translation arch,
diff --git a/core/fuzz_test.mk b/core/fuzz_test.mk
index fc582b3..c31e2e7 100644
--- a/core/fuzz_test.mk
+++ b/core/fuzz_test.mk
@@ -2,6 +2,7 @@
 ## A thin wrapper around BUILD_EXECUTABLE
 ## Common flags for fuzz tests are added.
 ###########################################
+$(call record-module-type,FUZZ_TEST)
 
 ifdef LOCAL_SDK_VERSION
     $(error $(LOCAL_PATH): $(LOCAL_MODULE): NDK fuzz tests are not supported.)
diff --git a/core/host_dalvik_java_library.mk b/core/host_dalvik_java_library.mk
index 7fdf249..cee3cc4 100644
--- a/core/host_dalvik_java_library.mk
+++ b/core/host_dalvik_java_library.mk
@@ -13,6 +13,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 #
+$(call record-module-type,HOST_DALVIK_JAVA_LIBRARY)
 
 #
 # Rules for building a host dalvik java library. These libraries
diff --git a/core/host_dalvik_static_java_library.mk b/core/host_dalvik_static_java_library.mk
index b79c0ea..78faf73 100644
--- a/core/host_dalvik_static_java_library.mk
+++ b/core/host_dalvik_static_java_library.mk
@@ -13,6 +13,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 #
+$(call record-module-type,HOST_DALVIK_STATIC_JAVA_LIBRARY)
 
 #
 # Rules for building a host dalvik static java library.
diff --git a/core/host_executable.mk b/core/host_executable.mk
index 0060c3e..78223eb 100644
--- a/core/host_executable.mk
+++ b/core/host_executable.mk
@@ -1,3 +1,4 @@
+$(call record-module-type,HOST_EXECUTABLE)
 LOCAL_IS_HOST_MODULE := true
 my_prefix := HOST_
 LOCAL_HOST_PREFIX :=
diff --git a/core/host_fuzz_test.mk b/core/host_fuzz_test.mk
index cc7baad..1c9eed2 100644
--- a/core/host_fuzz_test.mk
+++ b/core/host_fuzz_test.mk
@@ -2,6 +2,7 @@
 ## A thin wrapper around BUILD_HOST_EXECUTABLE
 ## Common flags for host fuzz tests are added.
 ################################################
+$(call record-module-type,HOST_FUZZ_TEST)
 
 LOCAL_CFLAGS += -fsanitize-coverage=edge,indirect-calls,8bit-counters,trace-cmp
 LOCAL_STATIC_LIBRARIES += libLLVMFuzzer
diff --git a/core/host_java_library.mk b/core/host_java_library.mk
index 9aa2a7c..0a7dd8a 100644
--- a/core/host_java_library.mk
+++ b/core/host_java_library.mk
@@ -14,6 +14,8 @@
 # limitations under the License.
 #
 
+$(call record-module-type,HOST_JAVA_LIBRARY)
+
 #
 # Standard rules for building a host java library.
 #
diff --git a/core/host_native_test.mk b/core/host_native_test.mk
index 2a6097d..aa05bb3 100644
--- a/core/host_native_test.mk
+++ b/core/host_native_test.mk
@@ -2,6 +2,7 @@
 ## A thin wrapper around BUILD_HOST_EXECUTABLE
 ## Common flags for host native tests are added.
 ################################################
+$(call record-module-type,HOST_NATIVE_TEST)
 
 ifdef LOCAL_MODULE_CLASS
 ifneq ($(LOCAL_MODULE_CLASS),NATIVE_TESTS)
diff --git a/core/host_prebuilt.mk b/core/host_prebuilt.mk
index 7baab69..79f3ffa 100644
--- a/core/host_prebuilt.mk
+++ b/core/host_prebuilt.mk
@@ -14,5 +14,6 @@
 # limitations under the License.
 #
 
+$(call record-module-type,HOST_PREBUILT)
 LOCAL_IS_HOST_MODULE := true
 include $(BUILD_MULTI_PREBUILT)
diff --git a/core/host_shared_library.mk b/core/host_shared_library.mk
index 2e0c9f1c..df24b63 100644
--- a/core/host_shared_library.mk
+++ b/core/host_shared_library.mk
@@ -1,3 +1,4 @@
+$(call record-module-type,HOST_SHARED_LIBRARY)
 LOCAL_IS_HOST_MODULE := true
 my_prefix := HOST_
 LOCAL_HOST_PREFIX :=
diff --git a/core/host_static_library.mk b/core/host_static_library.mk
index 068c702..61f5569 100644
--- a/core/host_static_library.mk
+++ b/core/host_static_library.mk
@@ -1,3 +1,4 @@
+$(call record-module-type,HOST_STATIC_LIBRARY)
 LOCAL_IS_HOST_MODULE := true
 my_prefix := HOST_
 LOCAL_HOST_PREFIX :=
diff --git a/core/host_static_test_lib.mk b/core/host_static_test_lib.mk
index 5423dc6..a24cd62 100644
--- a/core/host_static_test_lib.mk
+++ b/core/host_static_test_lib.mk
@@ -2,6 +2,7 @@
 ## A thin wrapper around BUILD_HOST_STATIC_LIBRARY
 ## Common flags for host native tests are added.
 ##################################################
+$(call record-module-type,HOST_STATIC_TEST_LIBRARY)
 
 include $(BUILD_SYSTEM)/host_test_internal.mk
 
diff --git a/core/java_library.mk b/core/java_library.mk
index 283e9ad..b132fa6 100644
--- a/core/java_library.mk
+++ b/core/java_library.mk
@@ -2,6 +2,7 @@
 ## Standard rules for building a java library.
 ##
 ###########################################################
+$(call record-module-type,JAVA_LIBRARY)
 
 ifdef LOCAL_IS_HOST_MODULE
 $(error $(LOCAL_PATH): Host java libraries must use BUILD_HOST_JAVA_LIBRARY)
diff --git a/core/main.mk b/core/main.mk
index b99de49..59e9524 100644
--- a/core/main.mk
+++ b/core/main.mk
@@ -1125,4 +1125,7 @@
 tidy_only:
 	@echo Successfully make tidy_only.
 
+ndk: $(SOONG_OUT_DIR)/ndk.timestamp
+.PHONY: ndk
+
 endif # KATI
diff --git a/core/multi_prebuilt.mk b/core/multi_prebuilt.mk
index ed2fed6..77c57ab 100644
--- a/core/multi_prebuilt.mk
+++ b/core/multi_prebuilt.mk
@@ -14,6 +14,7 @@
 # limitations under the License.
 #
 
+$(call record-module-type,MULTI_PREBUILT)
 ifneq ($(LOCAL_MODULE)$(LOCAL_MODULE_CLASS),)
 $(error $(LOCAL_PATH): LOCAL_MODULE or LOCAL_MODULE_CLASS not needed by \
   BUILD_MULTI_PREBUILT, use BUILD_PREBUILT instead!)
diff --git a/core/native_benchmark.mk b/core/native_benchmark.mk
index fe378b8..7b04d87 100644
--- a/core/native_benchmark.mk
+++ b/core/native_benchmark.mk
@@ -2,6 +2,7 @@
 ## A thin wrapper around BUILD_EXECUTABLE
 ## Common flags for native benchmarks are added.
 ###########################################
+$(call record-module-type,NATIVE_BENCHMARK)
 
 LOCAL_STATIC_LIBRARIES += libgoogle-benchmark
 
diff --git a/core/native_test.mk b/core/native_test.mk
index bb93eb0..8b49fbd 100644
--- a/core/native_test.mk
+++ b/core/native_test.mk
@@ -2,6 +2,7 @@
 ## A thin wrapper around BUILD_EXECUTABLE
 ## Common flags for native tests are added.
 ###########################################
+$(call record-module-type,NATIVE_TEST)
 
 ifdef LOCAL_MODULE_CLASS
 ifneq ($(LOCAL_MODULE_CLASS),NATIVE_TESTS)
diff --git a/core/notice_files.mk b/core/notice_files.mk
index e7f8974..f0013c2 100644
--- a/core/notice_files.mk
+++ b/core/notice_files.mk
@@ -1,6 +1,7 @@
 ###########################################################
 ## Track NOTICE files
 ###########################################################
+$(call record-module-type,NOTICE_FILE)
 
 ifneq ($(LOCAL_NOTICE_FILE),)
 notice_file:=$(strip $(LOCAL_NOTICE_FILE))
diff --git a/core/package.mk b/core/package.mk
index 8c2c435..4fe058d 100644
--- a/core/package.mk
+++ b/core/package.mk
@@ -2,6 +2,8 @@
 # TARGET_ARCH and TARGET_2ND_ARCH.
 # To build it for TARGET_2ND_ARCH in a 64bit product, use "LOCAL_MULTILIB := 32".
 
+$(call record-module-type,PACKAGE)
+
 ifeq ($(TARGET_TRANSLATE_2ND_ARCH),true)
 LOCAL_MULTILIB := first
 endif
diff --git a/core/phony_package.mk b/core/phony_package.mk
index b534335..578d629 100644
--- a/core/phony_package.mk
+++ b/core/phony_package.mk
@@ -1,3 +1,4 @@
+$(call record-module-type,PHONY_PACKAGE)
 ifneq ($(strip $(LOCAL_SRC_FILES)),)
 $(error LOCAL_SRC_FILES are not allowed for phony packages)
 endif
diff --git a/core/prebuilt.mk b/core/prebuilt.mk
index cbe2079..5831e24 100644
--- a/core/prebuilt.mk
+++ b/core/prebuilt.mk
@@ -5,6 +5,7 @@
 ## None.
 ##
 ###########################################################
+$(call record-module-type,PREBUILT)
 
 ifdef LOCAL_IS_HOST_MODULE
   my_prefix := HOST_
diff --git a/core/prebuilt_internal.mk b/core/prebuilt_internal.mk
index 90e9547..701414a 100644
--- a/core/prebuilt_internal.mk
+++ b/core/prebuilt_internal.mk
@@ -46,9 +46,18 @@
   $(LOCAL_PACK_MODULE_RELOCATIONS))
 
 ifeq (SHARED_LIBRARIES,$(LOCAL_MODULE_CLASS))
-  # Put the built targets of all shared libraries in a common directory
-  # to simplify the link line.
-  OVERRIDE_BUILT_MODULE_PATH := $($(LOCAL_2ND_ARCH_VAR_PREFIX)$(my_prefix)OUT_INTERMEDIATE_LIBRARIES)
+  # LOCAL_COPY_TO_INTERMEDIATE_LIBRARIES indicates that this prebuilt should be
+  # installed to the common directory of libraries. This is needed for the NDK
+  # shared libraries built by soong, as we build many different versions of each
+  # library (one for each API level). Since they all have the same basename,
+  # they'd clobber each other (as well as any platform libraries by the same
+  # name).
+  ifneq ($(LOCAL_COPY_TO_INTERMEDIATE_LIBRARIES),false)
+    # Put the built targets of all shared libraries in a common directory
+    # to simplify the link line.
+    OVERRIDE_BUILT_MODULE_PATH :=  \
+        $($(LOCAL_2ND_ARCH_VAR_PREFIX)$(my_prefix)OUT_INTERMEDIATE_LIBRARIES)
+  endif
   ifeq ($(LOCAL_IS_HOST_MODULE)$(my_strip_module),)
     # Strip but not try to add debuglink
     my_strip_module := no_debuglink
diff --git a/core/shared_library.mk b/core/shared_library.mk
index 2f48341..a15b1a6 100644
--- a/core/shared_library.mk
+++ b/core/shared_library.mk
@@ -1,3 +1,4 @@
+$(call record-module-type,SHARED_LIBRARY)
 my_prefix := TARGET_
 include $(BUILD_SYSTEM)/multilib.mk
 
diff --git a/core/soong.mk b/core/soong.mk
index c96ca2f..cb0a0d0 100644
--- a/core/soong.mk
+++ b/core/soong.mk
@@ -25,6 +25,13 @@
 	$(hide) mkdir -p $(dir $@)
 	$(hide) BUILDDIR=$(SOONG_OUT_DIR) ./bootstrap.bash
 
+BINDER32BIT :=
+ifneq ($(TARGET_USES_64_BIT_BINDER),true)
+ifneq ($(TARGET_IS_64_BIT),true)
+BINDER32BIT := true
+endif
+endif
+
 # Create soong.variables with copies of makefile settings.  Runs every build,
 # but only updates soong.variables if it changes
 SOONG_VARIABLES_TMP := $(SOONG_VARIABLES).$$$$
@@ -44,6 +51,7 @@
 	echo '    "HostStaticBinaries": $(if $(strip $(BUILD_HOST_static)),true,false),'; \
 	echo '    "Cpusets": $(if $(strip $(ENABLE_CPUSETS)),true,false),'; \
 	echo '    "Schedboost": $(if $(strip $(ENABLE_SCHEDBOOST)),true,false),'; \
+	echo '    "Binder32bit": $(if $(BINDER32BIT),true,false),'; \
 	echo ''; \
 	echo '    "DeviceName": "$(TARGET_DEVICE)",'; \
 	echo '    "DeviceArch": "$(TARGET_ARCH)",'; \
diff --git a/core/static_java_library.mk b/core/static_java_library.mk
index d3324f8..d6dd065 100644
--- a/core/static_java_library.mk
+++ b/core/static_java_library.mk
@@ -19,6 +19,7 @@
 # classpaths.  They can, however, be included wholesale in
 # other java modules.
 
+$(call record-module-type,STATIC_JAVA_LIBRARY)
 LOCAL_UNINSTALLABLE_MODULE := true
 LOCAL_IS_STATIC_JAVA_LIBRARY := true
 LOCAL_MODULE_CLASS := JAVA_LIBRARIES
diff --git a/core/static_library.mk b/core/static_library.mk
index a8ae399..25e5279 100644
--- a/core/static_library.mk
+++ b/core/static_library.mk
@@ -1,3 +1,4 @@
+$(call record-module-type,STATIC_LIBRARY)
 my_prefix := TARGET_
 include $(BUILD_SYSTEM)/multilib.mk
 
diff --git a/core/static_test_lib.mk b/core/static_test_lib.mk
index 9d0bcc8..a0e2970 100644
--- a/core/static_test_lib.mk
+++ b/core/static_test_lib.mk
@@ -2,6 +2,7 @@
 ## A thin wrapper around BUILD_STATIC_LIBRARY
 ## Common flags for native tests are added.
 #############################################
+$(call record-module-type,STATIC_TEST_LIBRARY)
 
 include $(BUILD_SYSTEM)/target_test_internal.mk
 
diff --git a/tools/releasetools/check_target_files_signatures.py b/tools/releasetools/check_target_files_signatures.py
index 5c541ab..3048488 100755
--- a/tools/releasetools/check_target_files_signatures.py
+++ b/tools/releasetools/check_target_files_signatures.py
@@ -245,7 +245,7 @@
             fullname = os.path.join(dirpath, fn)
             displayname = fullname[len(d)+1:]
             apk = APK(fullname, displayname)
-            self.apks[apk.package] = apk
+            self.apks[apk.filename] = apk
             self.apks_by_basename[os.path.basename(apk.filename)] = apk
 
             self.max_pkg_len = max(self.max_pkg_len, len(apk.package))
@@ -316,8 +316,7 @@
                                         self.max_pkg_len, apk.package,
                                         apk.shared_uid)
         else:
-          print "  %-*s  %-*s" % (self.max_fn_len, apk.filename,
-                                  self.max_pkg_len, apk.package)
+          print "  %-*s  %s" % (self.max_fn_len, apk.filename, apk.package)
       print
 
   def CompareWith(self, other):
diff --git a/tools/warn.py b/tools/warn.py
index 8f7a638..f17e8ac 100755
--- a/tools/warn.py
+++ b/tools/warn.py
@@ -6,6 +6,14 @@
 import re
 
 parser = argparse.ArgumentParser(description='Convert a build log into HTML')
+parser.add_argument('--gencsv',
+                    help='Generate a CSV file with number of various warnings',
+                    action="store_true",
+                    default=False)
+parser.add_argument('--byproject',
+                    help='Separate warnings in HTML output by project names',
+                    action="store_true",
+                    default=False)
 parser.add_argument('--url',
                     help='Root URL of an Android source code tree prefixed '
                     'before files in warnings')
@@ -1656,6 +1664,7 @@
     { 'category':'C/C++',   'severity':severity.MEDIUM,     'members':[], 'option':'',
         'description':'Undefined result',
         'patterns':[r".*: warning: The result of .+ is undefined",
+                    r".*: warning: passing an object that .+ has undefined behavior \[-Wvarargs\]",
                     r".*: warning: 'this' pointer cannot be null in well-defined C\+\+ code;",
                     r".*: warning: shifting a negative signed value is undefined"] },
     { 'category':'C/C++',   'severity':severity.MEDIUM,     'members':[], 'option':'',
@@ -1770,6 +1779,21 @@
         'description':'clang-tidy misc',
         'patterns':[r".*: .+\[misc-.+\]$"] },
     { 'category':'C/C++',   'severity':severity.TIDY,     'members':[], 'option':'',
+        'description':'clang-tidy performance-faster-string-find',
+        'patterns':[r".*: .+\[performance-faster-string-find\]$"] },
+    { 'category':'C/C++',   'severity':severity.TIDY,     'members':[], 'option':'',
+        'description':'clang-tidy performance-for-range-copy',
+        'patterns':[r".*: .+\[performance-for-range-copy\]$"] },
+    { 'category':'C/C++',   'severity':severity.TIDY,     'members':[], 'option':'',
+        'description':'clang-tidy performance-implicit-cast-in-loop',
+        'patterns':[r".*: .+\[performance-implicit-cast-in-loop\]$"] },
+    { 'category':'C/C++',   'severity':severity.TIDY,     'members':[], 'option':'',
+        'description':'clang-tidy performance-unnecessary-copy-initialization',
+        'patterns':[r".*: .+\[performance-unnecessary-copy-initialization\]$"] },
+    { 'category':'C/C++',   'severity':severity.TIDY,     'members':[], 'option':'',
+        'description':'clang-tidy performance-unnecessary-value-param',
+        'patterns':[r".*: .+\[performance-unnecessary-value-param\]$"] },
+    { 'category':'C/C++',   'severity':severity.TIDY,     'members':[], 'option':'',
         'description':'clang-tidy CERT',
         'patterns':[r".*: .+\[cert-.+\]$"] },
     { 'category':'C/C++',   'severity':severity.TIDY,     'members':[], 'option':'',
@@ -1789,6 +1813,63 @@
         'patterns':[r".*: warning: .+"] },
 ]
 
+# A list of [project_name, file_path_pattern].
+# project_name should not contain comma, to be used in CSV output.
+projectlist = [
+    ['art',                 r"(^|.*/)art/.*: warning:"],
+    ['bionic',              r"(^|.*/)bionic/.*: warning:"],
+    ['bootable',            r"(^|.*/)bootable/.*: warning:"],
+    ['build',               r"(^|.*/)build/.*: warning:"],
+    ['cts',                 r"(^|.*/)cts/.*: warning:"],
+    ['dalvik',              r"(^|.*/)dalvik/.*: warning:"],
+    ['developers',          r"(^|.*/)developers/.*: warning:"],
+    ['development',         r"(^|.*/)development/.*: warning:"],
+    ['device',              r"(^|.*/)device/.*: warning:"],
+    ['doc',                 r"(^|.*/)doc/.*: warning:"],
+    # match external/google* before external/
+    ['external/google',     r"(^|.*/)external/google.*: warning:"],
+    ['external/non-google', r"(^|.*/)external/.*: warning:"],
+    ['frameworks',          r"(^|.*/)frameworks/.*: warning:"],
+    ['hardware',            r"(^|.*/)hardware/.*: warning:"],
+    ['kernel',              r"(^|.*/)kernel/.*: warning:"],
+    ['libcore',             r"(^|.*/)libcore/.*: warning:"],
+    ['libnativehelper',      r"(^|.*/)libnativehelper/.*: warning:"],
+    ['ndk',                 r"(^|.*/)ndk/.*: warning:"],
+    ['packages',            r"(^|.*/)packages/.*: warning:"],
+    ['pdk',                 r"(^|.*/)pdk/.*: warning:"],
+    ['prebuilts',           r"(^|.*/)prebuilts/.*: warning:"],
+    ['system',              r"(^|.*/)system/.*: warning:"],
+    ['toolchain',           r"(^|.*/)toolchain/.*: warning:"],
+    ['test',                r"(^|.*/)test/.*: warning:"],
+    ['tools',               r"(^|.*/)tools/.*: warning:"],
+    # match vendor/google* before vendor/
+    ['vendor/google',       r"(^|.*/)vendor/google.*: warning:"],
+    ['vendor/non-google',   r"(^|.*/)vendor/.*: warning:"],
+    # keep out/obj and other patterns at the end.
+    ['out/obj', r".*/(gen|obj[^/]*)/(include|EXECUTABLES|SHARED_LIBRARIES|STATIC_LIBRARIES)/.*: warning:"],
+    ['other',   r".*: warning:"],
+]
+
+projectpatterns = []
+for p in projectlist:
+    projectpatterns.append({'description':p[0], 'members':[], 'pattern':re.compile(p[1])})
+
+# Each warning pattern has 3 dictionaries:
+# (1) 'projects' maps a project name to number of warnings in that project.
+# (2) 'projectanchor' maps a project name to its anchor number for HTML.
+# (3) 'projectwarning' maps a project name to a list of warning of that project.
+for w in warnpatterns:
+    w['projects'] = {}
+    w['projectanchor'] = {}
+    w['projectwarning'] = {}
+
+platformversion = 'unknown'
+targetproduct = 'unknown'
+targetvariant = 'unknown'
+
+
+##### Data and functions to dump html file. ##################################
+
 anchor = 0
 cur_row_class = 0
 
@@ -1837,7 +1918,6 @@
     output('<title>' + title + '</title>\n')
     output(html_script_style)
     output('</head>\n<body>\n')
-    output('<a name="PageTop">')
     output(htmlbig(title))
     output('<p>\n')
 
@@ -1851,12 +1931,16 @@
     output(text)
     output('</td></tr>')
 
+def sortwarnings():
+    for i in warnpatterns:
+        i['members'] = sorted(set(i['members']))
+
 # dump some stats about total number of warnings and such
 def dumpstats():
     known = 0
     unknown = 0
+    sortwarnings()
     for i in warnpatterns:
-        i['members'] = sorted(set(i['members']))
         if i['severity'] == severity.UNKNOWN:
             unknown += len(i['members'])
         elif i['severity'] != severity.SKIP:
@@ -1882,17 +1966,12 @@
     output('<blockquote>\n')
     for i in warnpatterns:
       if i['severity'] == sev and len(i['members']) > 0:
-          output('\n<table frame="box">\n')
           anchor += 1
           i['anchor'] = str(anchor)
-          mark = str(anchor) + '_mark'
-          output('<tr bgcolor="' + colorforseverity(sev) + '">' +
-                 '<td><button class="bt" id="' + mark +
-                 '" onclick="expand(\'' + str(anchor) + '\');">' +
-                 '&#x2295</button> ' + descriptionfor(i) +
-                 ' (' + str(len(i['members'])) + ')</td></tr>\n')
-          output('</table>\n')
-          dumpcategory(i)
+          if args.byproject:
+              dumpcategorybyproject(sev, i)
+          else:
+              dumpcategory(sev, i)
     output('</blockquote>\n')
 
 def allpatterns(cat):
@@ -1947,31 +2026,69 @@
     else:
         return '<a href="' + args.url + '/' + filepath + '">' + filepath + '</a>:' + linenumber + ':' + warning
 
-# dump a category, provided it is not marked as 'SKIP' and has more than 0 occurrences
-def dumpcategory(cat):
-    if cat['severity'] != severity.SKIP and len(cat['members']) != 0:
-        header = [descriptionfor(cat),str(len(cat['members'])) + ' occurences:']
-        if cat['option'] != '':
-            header[1:1] = [' (related option: ' + cat['option'] +')']
+def dumpgroup(sev, anchor, description, warnings):
+    mark = anchor + '_mark'
+    output('\n<table frame="box">\n')
+    output('<tr bgcolor="' + colorforseverity(sev) + '">' +
+           '<td><button class="bt" id="' + mark +
+           '" onclick="expand(\'' + anchor + '\');">' +
+           '&#x2295</button> ' + description + '</td></tr>\n')
+    output('</table>\n')
+    output('<div id="' + anchor + '" style="display:none;">')
+    output('<table>\n')
+    for i in warnings:
+        tablerow(warningwithurl(i))
+    output('</table></div>\n')
 
-        output('<div id="' + cat['anchor'] + '" style="display:none;">')
-        output('<table>\n')
-        for i in cat['members']:
-            tablerow(warningwithurl(i))
-        output('</table></div>\n')
+# dump warnings in a category
+def dumpcategory(sev, cat):
+    description = descriptionfor(cat) + ' (' + str(len(cat['members'])) + ')'
+    dumpgroup(sev, cat['anchor'], description, cat['members'])
 
+# similar to dumpcategory but output one table per project.
+def dumpcategorybyproject(sev, cat):
+    warning = descriptionfor(cat)
+    projects = cat['projectwarning'].keys()
+    projects.sort()
+    for p in projects:
+        anchor = cat['projectanchor'][p]
+        projectwarnings = cat['projectwarning'][p]
+        description = '{}, in {} ({})'.format(warning, p, len(projectwarnings))
+        dumpgroup(sev, anchor, description, projectwarnings)
+
+def findproject(line):
+    for p in projectpatterns:
+        if p['pattern'].match(line):
+            return p['description']
+    return '???'
 
 def classifywarning(line):
+    global anchor
     for i in warnpatterns:
         for cpat in i['compiledpatterns']:
             if cpat.match(line):
                 i['members'].append(line)
+                pname = findproject(line)
+                # Count warnings by project.
+                if pname in i['projects']:
+                    i['projects'][pname] += 1
+                else:
+                    i['projects'][pname] = 1
+                # Collect warnings by project.
+                if args.byproject:
+                    if pname in i['projectwarning']:
+                        i['projectwarning'][pname].append(line)
+                    else:
+                        i['projectwarning'][pname] = [line]
+                    if pname not in i['projectanchor']:
+                        anchor += 1
+                        i['projectanchor'][pname] = str(anchor)
                 return
-    else:
-        # If we end up here, there was a problem parsing the log
-        # probably caused by 'make -j' mixing the output from
-        # 2 or more concurrent compiles
-        pass
+            else:
+                # If we end up here, there was a problem parsing the log
+                # probably caused by 'make -j' mixing the output from
+                # 2 or more concurrent compiles
+                pass
 
 # precompiling every pattern speeds up parsing by about 30x
 def compilepatterns():
@@ -1980,54 +2097,103 @@
         for pat in i['patterns']:
             i['compiledpatterns'].append(re.compile(pat))
 
-infile = open(args.buildlog, 'r')
-warnings = []
+def parseinputfile():
+    global platformversion
+    global targetproduct
+    global targetvariant
+    infile = open(args.buildlog, 'r')
+    linecounter = 0
 
-platformversion = 'unknown'
-targetproduct = 'unknown'
-targetvariant = 'unknown'
-linecounter = 0
+    warningpattern = re.compile('.* warning:.*')
+    compilepatterns()
 
-warningpattern = re.compile('.* warning:.*')
-compilepatterns()
-
-# read the log file and classify all the warnings
-warninglines = set()
-for line in infile:
-    # replace fancy quotes with plain ol' quotes
-    line = line.replace("‘", "'");
-    line = line.replace("’", "'");
-    if warningpattern.match(line):
-        if line not in warninglines:
-            classifywarning(line)
-            warninglines.add(line)
-    else:
-        # save a little bit of time by only doing this for the first few lines
-        if linecounter < 50:
-            linecounter +=1
-            m = re.search('(?<=^PLATFORM_VERSION=).*', line)
-            if m != None:
-                platformversion = m.group(0)
-            m = re.search('(?<=^TARGET_PRODUCT=).*', line)
-            if m != None:
-                targetproduct = m.group(0)
-            m = re.search('(?<=^TARGET_BUILD_VARIANT=).*', line)
-            if m != None:
-                targetvariant = m.group(0)
+    # read the log file and classify all the warnings
+    warninglines = set()
+    for line in infile:
+        # replace fancy quotes with plain ol' quotes
+        line = line.replace("‘", "'");
+        line = line.replace("’", "'");
+        if warningpattern.match(line):
+            if line not in warninglines:
+                classifywarning(line)
+                warninglines.add(line)
+        else:
+            # save a little bit of time by only doing this for the first few lines
+            if linecounter < 50:
+                linecounter +=1
+                m = re.search('(?<=^PLATFORM_VERSION=).*', line)
+                if m != None:
+                    platformversion = m.group(0)
+                m = re.search('(?<=^TARGET_PRODUCT=).*', line)
+                if m != None:
+                    targetproduct = m.group(0)
+                m = re.search('(?<=^TARGET_BUILD_VARIANT=).*', line)
+                if m != None:
+                    targetvariant = m.group(0)
 
 
 # dump the html output to stdout
-dumphtmlprologue('Warnings for ' + platformversion + ' - ' + targetproduct + ' - ' + targetvariant)
-dumpstats()
-# sort table based on number of members once dumpstats has deduplicated the
-# members.
-warnpatterns.sort(reverse=True, key=lambda i: len(i['members']))
-dumpseverity(severity.FIXMENOW)
-dumpseverity(severity.HIGH)
-dumpseverity(severity.MEDIUM)
-dumpseverity(severity.LOW)
-dumpseverity(severity.TIDY)
-dumpseverity(severity.HARMLESS)
-dumpseverity(severity.UNKNOWN)
-dumpfixed()
-dumphtmlepilogue()
+def dumphtml():
+    dumphtmlprologue('Warnings for ' + platformversion + ' - ' + targetproduct + ' - ' + targetvariant)
+    dumpstats()
+    # sort table based on number of members once dumpstats has deduplicated the
+    # members.
+    warnpatterns.sort(reverse=True, key=lambda i: len(i['members']))
+    dumpseverity(severity.FIXMENOW)
+    dumpseverity(severity.HIGH)
+    dumpseverity(severity.MEDIUM)
+    dumpseverity(severity.LOW)
+    dumpseverity(severity.TIDY)
+    dumpseverity(severity.HARMLESS)
+    dumpseverity(severity.UNKNOWN)
+    dumpfixed()
+    dumphtmlepilogue()
+
+
+##### Functions to count warnings and dump csv file. #########################
+
+def descriptionforcsv(cat):
+    if cat['description'] == '':
+        return '?'
+    return cat['description']
+
+def stringforcsv(s):
+    if ',' in s:
+        return '"{}"'.format(s)
+    return s
+
+def countseverity(sev, kind):
+  sum = 0
+  for i in warnpatterns:
+      if i['severity'] == sev and len(i['members']) > 0:
+          n = len(i['members'])
+          sum += n
+          warning = stringforcsv(kind + ': ' + descriptionforcsv(i))
+          print '{},,{}'.format(n, warning)
+          # print number of warnings for each project, ordered by project name.
+          projects = i['projects'].keys()
+          projects.sort()
+          for p in projects:
+              print '{},{},{}'.format(i['projects'][p], p, warning)
+  print '{},,{}'.format(sum, kind + ' warnings')
+  return sum
+
+# dump number of warnings in csv format to stdout
+def dumpcsv():
+    sortwarnings()
+    total = 0
+    total += countseverity(severity.FIXMENOW, 'FixNow')
+    total += countseverity(severity.HIGH, 'High')
+    total += countseverity(severity.MEDIUM, 'Medium')
+    total += countseverity(severity.LOW, 'Low')
+    total += countseverity(severity.TIDY, 'Tidy')
+    total += countseverity(severity.HARMLESS, 'Harmless')
+    total += countseverity(severity.UNKNOWN, 'Unknown')
+    print '{},,{}'.format(total, 'All warnings')
+
+
+parseinputfile()
+if args.gencsv:
+    dumpcsv()
+else:
+    dumphtml()