Use .KATI_RESTAT to reduce unnecessary rebuilds of .jar files

The same optimization was done for binaries in

https://android-review.googlesource.com/#/c/175250/

To create a TOC file from .jar files, this change introduces
ijar, which is designed for this purpose. Only #include lines
were modified from the original version.

https://github.com/bazelbuild/bazel/tree/master/third_party/ijar

Performance:

$ m && touch
frameworks/base/core/java/com/google/android/util/Procedure.java && time
m
Before: 4m30s (1580 targets)
After: 3m57s (772 targets)

Unfortunately, the improvement is small yet, but local
experiments showed we can cut ~2 more minutes if the similar
optimization is done for .dex files.

(cherry picked from commit c1f5d9c203324b9435414e789c02394ca99c98f7)

Bug: 24597504
Change-Id: Iec3b2b0b0e674bee5d80cce3c300dc8fad6e7c13
diff --git a/core/base_rules.mk b/core/base_rules.mk
index 42dfc1b..5ee5edf 100644
--- a/core/base_rules.mk
+++ b/core/base_rules.mk
@@ -223,25 +223,15 @@
 ###########################################################
 ## Create .toc files from shared objects to reduce unnecessary rebuild
 # .toc files have the list of external dynamic symbols without their addresses.
-# For ninja build, .toc files will be updated only when the content of .toc
-# files are changed. As .KATI_RESTAT is specified to .toc files, dependent
-# binaries of a .toc file will be rebuilt only when the content of
+# As .KATI_RESTAT is specified to .toc files and commit-change-for-toc is used,
+# dependent binaries of a .toc file will be rebuilt only when the content of
 # the .toc file is changed.
 ###########################################################
 ifeq ($(LOCAL_MODULE_CLASS),SHARED_LIBRARIES)
 LOCAL_INTERMEDIATE_TARGETS += $(LOCAL_BUILT_MODULE).toc
 $(LOCAL_BUILT_MODULE).toc: $(LOCAL_BUILT_MODULE)
-ifeq ($(BUILDING_WITH_NINJA),true)
 	$(call $(PRIVATE_2ND_ARCH_VAR_PREFIX)$(PRIVATE_PREFIX)transform-shared-lib-to-toc,$<,$@.tmp)
-	$(hide) if cmp -s $@.tmp $@ ; then \
-		  rm $@.tmp ; \
-		else \
-		  mv $@.tmp $@ ; \
-		fi
-else
-	@# make doesn't support restat. We always update .toc files so the dependents will always be updated too.
-	$(call $(PRIVATE_2ND_ARCH_VAR_PREFIX)$(PRIVATE_PREFIX)transform-shared-lib-to-toc,$<,$@)
-endif
+	$(call commit-change-for-toc,$@)
 
 # Kati adds restat=1 to ninja. GNU make does nothing for this.
 .KATI_RESTAT: $(LOCAL_BUILT_MODULE).toc
diff --git a/core/config.mk b/core/config.mk
index 49810ee..e615159 100644
--- a/core/config.mk
+++ b/core/config.mk
@@ -540,6 +540,10 @@
 ZIPTIME := $(HOST_OUT_EXECUTABLES)/ziptime$(HOST_EXECUTABLE_SUFFIX)
 endif
 
+# ijar converts a .jar file to a smaller .jar file which only has its
+# interfaces.
+IJAR := $(HOST_OUT_EXECUTABLES)/ijar$(BUILD_EXECUTABLE_SUFFIX)
+
 # relocation packer
 RELOCATION_PACKER := prebuilts/misc/$(BUILD_OS)-$(HOST_PREBUILT_ARCH)/relocation_packer/relocation_packer
 
diff --git a/core/definitions.mk b/core/definitions.mk
index e6f4a04..89ffcf7 100644
--- a/core/definitions.mk
+++ b/core/definitions.mk
@@ -1928,6 +1928,42 @@
 	$(hide) rm $@.tmpjill.jack
 endef
 
+# Moves $1.tmp to $1 if necessary. This is designed to be used with
+# .KATI_RESTAT. For kati, this function doesn't update the timestamp
+# of $1 when $1.tmp is identical to $1 so that ninja won't rebuild
+# targets which depend on $1. For GNU make, this function simply
+# copies $1.tmp to $1.
+ifeq ($(BUILDING_WITH_NINJA),true)
+define commit-change-for-toc
+$(hide) if cmp -s $1.tmp $1 ; then \
+ rm $1.tmp ; \
+else \
+ mv $1.tmp $1 ; \
+fi
+endef
+else
+define commit-change-for-toc
+@# make doesn't support restat. We always update .toc files so the dependents will always be updated too.
+$(hide) mv $1.tmp $1
+endef
+endif
+
+## Rule to creates a table of contents from a .jar file.
+## Must be called with $(eval).
+# $1: A .jar file
+define _transform-jar-to-toc
+$1.toc: $1 | $(IJAR)
+	@echo Generating TOC: $$@
+	$(hide) $(IJAR) $$< $$@.tmp
+	$$(call commit-change-for-toc,$$@)
+endef
+
+## Define a rule which generates .jar.toc and mark it as .KATI_RESTAT.
+define define-jar-to-toc-rule
+$(eval $(call _transform-jar-to-toc,$1))
+$(eval .KATI_RESTAT: $1.toc)
+endef
+
 
 # Invoke Jack to compile java from source to jack files without shrink or obfuscation.
 #
diff --git a/core/java.mk b/core/java.mk
index f9305df..9bdb77f 100644
--- a/core/java.mk
+++ b/core/java.mk
@@ -456,6 +456,8 @@
 	@echo Copying: $@
 	$(hide) $(ACP) -fp $< $@
 
+$(call define-jar-to-toc-rule, $(full_classes_jar))
+
 # Run proguard if necessary, otherwise just copy the file.
 ifdef LOCAL_PROGUARD_ENABLED
 ifneq ($(filter-out full custom nosystem obfuscation optimization shrinktests,$(LOCAL_PROGUARD_ENABLED)),)
diff --git a/core/java_common.mk b/core/java_common.mk
index 9d81096..b7f07ab 100644
--- a/core/java_common.mk
+++ b/core/java_common.mk
@@ -147,6 +147,7 @@
 
 full_shared_java_libs := $(call java-lib-files,$(LOCAL_JAVA_LIBRARIES),$(LOCAL_IS_HOST_MODULE))
 full_java_lib_deps := $(call java-lib-deps,$(LOCAL_JAVA_LIBRARIES),$(LOCAL_IS_HOST_MODULE))
+full_java_lib_deps := $(addsuffix .toc, $(full_java_lib_deps))
 
 else # LOCAL_IS_HOST_MODULE
 
diff --git a/core/prebuilt_internal.mk b/core/prebuilt_internal.mk
index da76eb9..aec08ed 100644
--- a/core/prebuilt_internal.mk
+++ b/core/prebuilt_internal.mk
@@ -315,6 +315,8 @@
 $(common_javalib_jar) : $(common_classes_jar) | $(ACP)
 	$(transform-prebuilt-to-target)
 
+$(call define-jar-to-toc-rule, $(common_classes_jar))
+
 # make sure the classes.jar and javalib.jar are built before $(LOCAL_BUILT_MODULE)
 $(built_module) : $(common_javalib_jar)
 endif # TARGET JAVA_LIBRARIES