Auto-generate dexpreopt boot image module

Add prebuilt modules that installs bootjar dexpreopt files generated by
soong, for example `dexpreopt_bootjar.boot_arm64` installs .art / .oat /
.vdex, etc for the framework boot image ("boot") for primary arch
("arm64").

Bug: 159196136
Test: TH built artifacts noop
Test: Check device boot images are installed
Test: ./prebuilts/build-tools/linux-x86/bin/ninja -f out/combined-aosp_arm64.ninja -t browse
Test: Check that generated dependency graph is sane
Test: Check that bootjar dexpreopt bits are listed in `m dump-files`
Change-Id: Ia30b37be9be972c5e28b5840923801ea7237a237
diff --git a/core/dex_preopt_libart.mk b/core/dex_preopt_libart.mk
index 12b29f4..8f0702b 100644
--- a/core/dex_preopt_libart.mk
+++ b/core/dex_preopt_libart.mk
@@ -5,38 +5,75 @@
 #   my_boot_image_arch: the architecture to install (e.g. TARGET_ARCH, not expanded)
 #   my_boot_image_out:  the install directory (e.g. $(PRODUCT_OUT))
 #   my_boot_image_syms: the symbols director (e.g. $(TARGET_OUT_UNSTRIPPED))
-#   my_boot_image_root: make variable used to store installed image path
+#
+# Output variables:
+#   my_boot_image_module: the created module name. Empty if no module is created.
+#
+# Install the boot images compiled by Soong.
+# Create a module named dexpreopt_bootjar.$(my_boot_image_name)_$($(my_boot_image_arch))
+# that installs all of boot image files.
+# If there is no file to install for $(my_boot_image_name), for example when
+# building an unbundled build, then no module is created.
 #
 ####################################
 
 # Install $(1) to $(2) so that it is shared between architectures.
+# Returns the target path of the shared vdex file and installed symlink.
 define copy-vdex-file
-my_vdex_shared := $$(dir $$(patsubst %/,%,$$(dir $(2))))$$(notdir $(2))  # Remove the arch dir.
-ifneq ($(my_boot_image_arch),$(filter $(my_boot_image_arch), TARGET_2ND_ARCH HOST_2ND_ARCH))
-$$(my_vdex_shared): $(1)  # Copy $(1) to directory one level up (i.e. with the arch dir removed).
-	@echo "Install: $$@"
-	$$(copy-file-to-target)
-endif
-$(2): $$(my_vdex_shared)  # Create symlink at $(2) which points to the actual physical copy.
-	@echo "Symlink: $$@"
-	mkdir -p $$(dir $$@)
-	ln -sfn ../$$(notdir $$@) $$@
-my_vdex_shared :=
+$(strip \
+  $(eval # Remove the arch dir) \
+  $(eval my_vdex_shared := $(dir $(patsubst %/,%,$(dir $(2))))$(notdir $(2))) \
+  $(if $(filter-out %_2ND_ARCH,$(my_boot_image_arch)), \
+    $(eval # Copy $(1) to directory one level up (i.e. with the arch dir removed).) \
+    $(eval $(call copy-one-file,$(1),$(my_vdex_shared))) \
+  ) \
+  $(eval # Create symlink at $(2) which points to the actual physical copy.) \
+  $(call symlink-file,$(my_vdex_shared),../$(notdir $(2)),$(2)) \
+  $(my_vdex_shared) $(2) \
+)
 endef
 
 # Same as 'copy-many-files' but it uses the vdex-specific helper above.
 define copy-vdex-files
-$(foreach v,$(1),$(eval $(call copy-vdex-file, $(call word-colon,1,$(v)), $(2)$(call word-colon,2,$(v)))))
-$(foreach v,$(1),$(2)$(call word-colon,2,$(v)))
+$(foreach v,$(1),$(call copy-vdex-file,$(call word-colon,1,$(v)),$(2)$(call word-colon,2,$(v))))
 endef
 
-# Install the boot images compiled by Soong.
-# The first file is saved in $(my_boot_image_root) and the rest are added as it's dependencies.
-my_suffix := BUILT_INSTALLED_$(my_boot_image_name)_$($(my_boot_image_arch))
-my_installed := $(call copy-many-files,$(DEXPREOPT_IMAGE_$(my_suffix)),$(my_boot_image_out))
-my_installed += $(call copy-many-files,$(DEXPREOPT_IMAGE_UNSTRIPPED_$(my_suffix)),$(my_boot_image_syms))
-my_installed += $(call copy-vdex-files,$(DEXPREOPT_IMAGE_VDEX_$(my_suffix)),$(my_boot_image_out))
-$(my_boot_image_root) += $(firstword $(my_installed))
-$(firstword $(my_installed)): $(wordlist 2,9999,$(my_installed))
-my_installed :=
-my_suffix :=
+my_boot_image_module :=
+
+my_suffix := $(my_boot_image_name)_$($(my_boot_image_arch))
+my_copy_pairs := $(strip $(DEXPREOPT_IMAGE_BUILT_INSTALLED_$(my_suffix)))
+
+# Generate the boot image module only if there is any file to install.
+ifneq (,$(my_copy_pairs))
+  my_first_pair := $(firstword $(my_copy_pairs))
+  my_rest_pairs := $(wordlist 2,$(words $(my_copy_pairs)),$(my_copy_pairs))
+
+  my_first_src := $(call word-colon,1,$(my_first_pair))
+  my_first_dest := $(my_boot_image_out)$(call word-colon,2,$(my_first_pair))
+
+  my_installed := $(call copy-many-files,$(my_rest_pairs),$(my_boot_image_out))
+  my_installed += $(call copy-vdex-files,$(DEXPREOPT_IMAGE_VDEX_BUILT_INSTALLED_$(my_suffix)),$(my_boot_image_out))
+  my_unstripped_installed := $(call copy-many-files,$(DEXPREOPT_IMAGE_UNSTRIPPED_BUILT_INSTALLED_$(my_suffix)),$(my_boot_image_syms))
+
+  # We don't have a LOCAL_PATH for the auto-generated modules, so let it be the $(BUILD_SYSTEM).
+  LOCAL_PATH := $(BUILD_SYSTEM)
+
+  include $(CLEAR_VARS)
+  LOCAL_MODULE := dexpreopt_bootjar.$(my_suffix)
+  LOCAL_PREBUILT_MODULE_FILE := $(my_first_src)
+  LOCAL_MODULE_PATH := $(dir $(my_first_dest))
+  LOCAL_MODULE_STEM := $(notdir $(my_first_dest))
+  ifneq (,$(strip $(filter HOST_%,$(my_boot_image_arch))))
+    LOCAL_IS_HOST_MODULE := true
+  endif
+  LOCAL_MODULE_CLASS := ETC
+  include $(BUILD_PREBUILT)
+  $(LOCAL_BUILT_MODULE): $(my_unstripped_installed)
+  # Installing boot.art causes all boot image bits to be installed.
+  # Keep this old behavior in case anyone still needs it.
+  $(LOCAL_INSTALLED_MODULE): $(my_installed)
+  ALL_MODULES.$(my_register_name).INSTALLED += $(my_installed)
+  $(my_all_targets): $(my_installed)
+
+  my_boot_image_module := $(LOCAL_MODULE)
+endif  # my_copy_pairs != empty