Add build rules for statically linked oatdump on host.

Also extend oatdump_test to exercise oatdump(d)s.

Test: ART_BUILD_HOST_STATIC=true m test-art-host-gtest-oatdump_test
Bug: 29530992
Change-Id: I6eb6c96f385832733d18d0400abd9974a6d8e45c
diff --git a/build/Android.common_build.mk b/build/Android.common_build.mk
index b5d41d9..04a0344 100644
--- a/build/Android.common_build.mk
+++ b/build/Android.common_build.mk
@@ -238,6 +238,12 @@
   art_cflags += -DART_USE_TLAB=1
 endif
 
+# Are additional statically-linked ART host binaries (dex2oats,
+# oatdumps, etc.) getting built?
+ifeq ($(ART_BUILD_HOST_STATIC),true)
+  art_cflags += -DART_BUILD_HOST_STATIC=1
+endif
+
 # Cflags for non-debug ART and ART tools.
 art_non_debug_cflags := \
   $(ART_NDEBUG_OPT_FLAG)
diff --git a/build/Android.executable.mk b/build/Android.executable.mk
index 8576916..4db3533 100644
--- a/build/Android.executable.mk
+++ b/build/Android.executable.mk
@@ -254,3 +254,20 @@
     )
   )
 endef
+
+# Note: the order is important because of static linking resolution.
+ART_STATIC_DEPENDENCIES := \
+  libziparchive \
+  libnativehelper \
+  libnativebridge \
+  libnativeloader \
+  libsigchain_dummy \
+  liblog \
+  libz \
+  libbacktrace \
+  libcutils \
+  libunwindbacktrace \
+  libutils \
+  libbase \
+  liblz4 \
+  liblzma
diff --git a/build/art.go b/build/art.go
index 4e64dcf..f305c07 100644
--- a/build/art.go
+++ b/build/art.go
@@ -67,6 +67,12 @@
 		cflags = append(cflags, "-fstack-protector")
 	}
 
+	// Are additional statically-linked ART host binaries
+	// (dex2oats, oatdumps, etc.) getting built?
+	if envTrue(ctx, "ART_BUILD_HOST_STATIC") {
+		cflags = append(cflags, "-DART_BUILD_HOST_STATIC=1")
+	}
+
 	return cflags, asflags
 }
 
diff --git a/dex2oat/Android.mk b/dex2oat/Android.mk
index 8a179c1..32424ca 100644
--- a/dex2oat/Android.mk
+++ b/dex2oat/Android.mk
@@ -55,35 +55,19 @@
   $(eval $(call build-art-executable,dex2oat,$(DEX2OAT_SRC_FILES),libcutils libartd-compiler libsigchain,art/compiler,target,debug,$(dex2oat_target_arch)))
 endif
 
-# Note: the order is important because of static linking resolution.
-DEX2OAT_STATIC_DEPENDENCIES := \
-  libziparchive \
-  libnativehelper \
-  libnativebridge \
-  libnativeloader \
-  libsigchain_dummy \
-  liblog \
-  libz \
-  libbacktrace \
-  libcutils \
-  libunwindbacktrace \
-  libutils \
-  libbase \
-  liblz4 \
-  liblzma
-
-# We always build dex2oat and dependencies, even if the host build is otherwise disabled, since they are used to cross compile for the target.
+# We always build dex2oat and dependencies, even if the host build is
+# otherwise disabled, since they are used to cross compile for the target.
 ifeq ($(ART_BUILD_HOST_NDEBUG),true)
   $(eval $(call build-art-executable,dex2oat,$(DEX2OAT_SRC_FILES),libcutils libart-compiler libsigchain libziparchive liblz4,art/compiler,host,ndebug,$(dex2oat_host_arch)))
   ifeq ($(ART_BUILD_HOST_STATIC),true)
-    $(eval $(call build-art-executable,dex2oat,$(DEX2OAT_SRC_FILES),libart libart-compiler libart libvixl-arm libvixl-arm64 $(DEX2OAT_STATIC_DEPENDENCIES),art/compiler,host,ndebug,$(dex2oat_host_arch),static))
+    $(eval $(call build-art-executable,dex2oat,$(DEX2OAT_SRC_FILES),libart libart-compiler libart libvixl-arm libvixl-arm64 $(ART_STATIC_DEPENDENCIES),art/compiler,host,ndebug,$(dex2oat_host_arch),static))
   endif
 endif
 
 ifeq ($(ART_BUILD_HOST_DEBUG),true)
   $(eval $(call build-art-executable,dex2oat,$(DEX2OAT_SRC_FILES),libcutils libartd-compiler libsigchain libziparchive liblz4,art/compiler,host,debug,$(dex2oat_host_arch)))
   ifeq ($(ART_BUILD_HOST_STATIC),true)
-    $(eval $(call build-art-executable,dex2oat,$(DEX2OAT_SRC_FILES),libartd libartd-compiler libartd libvixld-arm libvixld-arm64 $(DEX2OAT_STATIC_DEPENDENCIES),art/compiler,host,debug,$(dex2oat_host_arch),static))
+    $(eval $(call build-art-executable,dex2oat,$(DEX2OAT_SRC_FILES),libartd libartd-compiler libartd libvixld-arm libvixld-arm64 $(ART_STATIC_DEPENDENCIES),art/compiler,host,debug,$(dex2oat_host_arch),static))
   endif
 endif
 
diff --git a/disassembler/Android.mk b/disassembler/Android.mk
index db327fc..630f3e4 100644
--- a/disassembler/Android.mk
+++ b/disassembler/Android.mk
@@ -27,6 +27,7 @@
 
 # $(1): target or host
 # $(2): ndebug or debug
+# $(3): static or shared (static is only valid for host)
 define build-libart-disassembler
   ifneq ($(1),target)
     ifneq ($(1),host)
@@ -38,9 +39,19 @@
       $$(error expected ndebug or debug for argument 2, received $(2))
     endif
   endif
+  ifeq ($(3),static)
+    ifneq ($(1),host)
+      $$(error received static for argument 3, but argument 1 is not host)
+    endif
+  else
+    ifneq ($(3),shared)
+      $$(error expected static or shared for argument 3, received $(3))
+    endif
+  endif
 
   art_target_or_host := $(1)
   art_ndebug_or_debug := $(2)
+  art_static_or_shared := $(3)
 
   include $(CLEAR_VARS)
   ifeq ($$(art_target_or_host),host)
@@ -54,7 +65,11 @@
   endif
 
   LOCAL_MODULE_TAGS := optional
-  LOCAL_MODULE_CLASS := SHARED_LIBRARIES
+  ifeq ($$(art_static_or_shared),static)
+    LOCAL_MODULE_CLASS := STATIC_LIBRARIES
+  else # shared
+    LOCAL_MODULE_CLASS := SHARED_LIBRARIES
+  endif
 
   LOCAL_SRC_FILES := $$(LIBART_DISASSEMBLER_SRC_FILES)
 
@@ -74,11 +89,20 @@
     endif
   endif
 
-  LOCAL_SHARED_LIBRARIES += liblog
-  ifeq ($$(art_ndebug_or_debug),debug)
-    LOCAL_SHARED_LIBRARIES += libartd
-  else
-    LOCAL_SHARED_LIBRARIES += libart
+  ifeq ($$(art_static_or_shared),static)
+    LOCAL_STATIC_LIBRARIES += liblog
+    ifeq ($$(art_ndebug_or_debug),debug)
+      LOCAL_STATIC_LIBRARIES += libartd
+    else
+      LOCAL_STATIC_LIBRARIES += libart
+    endif
+  else # shared
+    LOCAL_SHARED_LIBRARIES += liblog
+    ifeq ($$(art_ndebug_or_debug),debug)
+      LOCAL_SHARED_LIBRARIES += libartd
+    else
+      LOCAL_SHARED_LIBRARIES += libart
+    endif
   endif
 
   LOCAL_C_INCLUDES += $(ART_C_INCLUDES) art/runtime
@@ -89,28 +113,53 @@
   LOCAL_ADDITIONAL_DEPENDENCIES += $(LOCAL_PATH)/Android.mk
   LOCAL_NATIVE_COVERAGE := $(ART_COVERAGE)
   # For disassembler_arm64.
-  ifeq ($$(art_ndebug_or_debug),debug)
-    LOCAL_SHARED_LIBRARIES += libvixld-arm64
-  else
-    LOCAL_SHARED_LIBRARIES += libvixl-arm64
+  ifeq ($$(art_static_or_shared),static)
+    ifeq ($$(art_ndebug_or_debug),debug)
+      LOCAL_STATIC_LIBRARIES += libvixld-arm64
+    else
+      LOCAL_STATIC_LIBRARIES += libvixl-arm64
+    endif
+    ifeq ($$(art_target_or_host),target)
+      $$(error libart-disassembler static builds for target are not supported)
+    else # host
+      include $(BUILD_HOST_STATIC_LIBRARY)
+    endif
+  else # shared
+    ifeq ($$(art_ndebug_or_debug),debug)
+      LOCAL_SHARED_LIBRARIES += libvixld-arm64
+    else
+      LOCAL_SHARED_LIBRARIES += libvixl-arm64
+    endif
+    ifeq ($$(art_target_or_host),target)
+      include $(BUILD_SHARED_LIBRARY)
+    else # host
+      include $(BUILD_HOST_SHARED_LIBRARY)
+    endif
   endif
-  ifeq ($$(art_target_or_host),target)
-    include $(BUILD_SHARED_LIBRARY)
-  else # host
-    include $(BUILD_HOST_SHARED_LIBRARY)
-  endif
+
+  # Clear out local variables now that we're done with them.
+  art_target_or_host :=
+  art_ndebug_or_debug :=
+  art_static_or_shared :=
 endef
 
 ifeq ($(ART_BUILD_TARGET_NDEBUG),true)
-  $(eval $(call build-libart-disassembler,target,ndebug))
+  $(eval $(call build-libart-disassembler,target,ndebug,shared))
 endif
 ifeq ($(ART_BUILD_TARGET_DEBUG),true)
-  $(eval $(call build-libart-disassembler,target,debug))
+  $(eval $(call build-libart-disassembler,target,debug,shared))
 endif
-# We always build dex2oat and dependencies, even if the host build is otherwise disabled, since they are used to cross compile for the target.
+# We always build dex2oat and dependencies, even if the host build is
+# otherwise disabled, since they are used to cross compile for the target.
 ifeq ($(ART_BUILD_HOST_NDEBUG),true)
-  $(eval $(call build-libart-disassembler,host,ndebug))
+  $(eval $(call build-libart-disassembler,host,ndebug,shared))
+  ifeq ($(ART_BUILD_HOST_STATIC),true)
+    $(eval $(call build-libart-disassembler,host,ndebug,static))
+  endif
 endif
 ifeq ($(ART_BUILD_HOST_DEBUG),true)
-  $(eval $(call build-libart-disassembler,host,debug))
+  $(eval $(call build-libart-disassembler,host,debug,shared))
+  ifeq ($(ART_BUILD_HOST_STATIC),true)
+    $(eval $(call build-libart-disassembler,host,debug,static))
+  endif
 endif
diff --git a/oatdump/Android.mk b/oatdump/Android.mk
index 5c75f20..4b37c6a 100644
--- a/oatdump/Android.mk
+++ b/oatdump/Android.mk
@@ -24,6 +24,28 @@
 # Build variants {target,host} x {debug,ndebug}
 $(eval $(call build-art-multi-executable,oatdump,$(OATDUMP_SRC_FILES),libart-compiler libart-disassembler,libcutils,,art/compiler art/disassembler))
 
+# Static variants (only for host).
+ifeq ($(ART_BUILD_HOST_STATIC),true)
+  ifeq ($(HOST_PREFER_32_BIT),true)
+    # We need to explicitly restrict the host arch to 32-bit only, as
+    # giving 'both' would make build-art-executable generate a build
+    # rule for a 64-bit oatdump executable too.
+    oatdump_host_arch := 32
+  else
+    oatdump_host_arch := both
+  endif
+
+  ifeq ($(ART_BUILD_HOST_NDEBUG),true)
+    $(eval $(call build-art-executable,oatdump,$(OATDUMP_SRC_FILES),libart libart-compiler libart-disassembler libvixl-arm64 $(ART_STATIC_DEPENDENCIES),art/compiler art/disassembler,host,ndebug,$(oatdump_host_arch),static))
+  endif
+  ifeq ($(ART_BUILD_HOST_DEBUG),true)
+    $(eval $(call build-art-executable,oatdump,$(OATDUMP_SRC_FILES),libartd libartd-compiler libartd-disassembler libvixld-arm64 $(ART_STATIC_DEPENDENCIES),art/compiler art/disassembler,host,debug,$(oatdump_host_arch),static))
+  endif
+
+  # Clear locals now they've served their purpose.
+  oatdump_host_arch :=
+endif
+
 ########################################################################
 # oatdump targets
 
diff --git a/oatdump/oatdump_test.cc b/oatdump/oatdump_test.cc
index db97055..22db818 100644
--- a/oatdump/oatdump_test.cc
+++ b/oatdump/oatdump_test.cc
@@ -42,13 +42,22 @@
     core_oat_location_ = GetSystemImageFilename(GetCoreOatLocation().c_str(), kRuntimeISA);
   }
 
+  // Linking flavor.
+  enum Flavor {
+    kDynamic,  // oatdump(d)
+    kStatic,   // oatdump(d)s
+  };
+
   // Returns path to the oatdump binary.
-  std::string GetOatDumpFilePath() {
+  std::string GetOatDumpFilePath(Flavor flavor) {
     std::string root = GetTestAndroidRoot();
     root += "/bin/oatdump";
     if (kIsDebugBuild) {
       root += "d";
     }
+    if (flavor == kStatic) {
+      root += "s";
+    }
     return root;
   }
 
@@ -58,12 +67,19 @@
     kModeSymbolize,
   };
 
+  // Display style.
+  enum Display {
+    kListOnly,
+    kListAndCode
+  };
+
   // Run the test with custom arguments.
-  bool Exec(Mode mode,
+  bool Exec(Flavor flavor,
+            Mode mode,
             const std::vector<std::string>& args,
-            bool list_only,
+            Display display,
             std::string* error_msg) {
-    std::string file_path = GetOatDumpFilePath();
+    std::string file_path = GetOatDumpFilePath(flavor);
 
     EXPECT_TRUE(OS::FileExists(file_path.c_str())) << file_path << " should be a valid file path";
 
@@ -81,7 +97,7 @@
       expected_prefixes.push_back("LOCATION:");
       expected_prefixes.push_back("MAGIC:");
       expected_prefixes.push_back("DEX FILE COUNT:");
-      if (!list_only) {
+      if (display == kListAndCode) {
         // Code and dex code do not show up if list only.
         expected_prefixes.push_back("DEX CODE:");
         expected_prefixes.push_back("CODE:");
@@ -205,37 +221,73 @@
 #if !defined(__arm__) && !defined(__mips__)
 TEST_F(OatDumpTest, TestImage) {
   std::string error_msg;
-  ASSERT_TRUE(Exec(kModeArt, {}, /*list_only*/ false, &error_msg)) << error_msg;
+  ASSERT_TRUE(Exec(kDynamic, kModeArt, {}, kListAndCode, &error_msg)) << error_msg;
+}
+TEST_F(OatDumpTest, TestImageStatic) {
+  TEST_DISABLED_FOR_NON_STATIC_HOST_BUILDS();
+  std::string error_msg;
+  ASSERT_TRUE(Exec(kStatic, kModeArt, {}, kListAndCode, &error_msg)) << error_msg;
 }
 
 TEST_F(OatDumpTest, TestOatImage) {
   std::string error_msg;
-  ASSERT_TRUE(Exec(kModeOat, {}, /*list_only*/ false, &error_msg)) << error_msg;
+  ASSERT_TRUE(Exec(kDynamic, kModeOat, {}, kListAndCode, &error_msg)) << error_msg;
+}
+TEST_F(OatDumpTest, TestOatImageStatic) {
+  TEST_DISABLED_FOR_NON_STATIC_HOST_BUILDS();
+  std::string error_msg;
+  ASSERT_TRUE(Exec(kStatic, kModeOat, {}, kListAndCode, &error_msg)) << error_msg;
 }
 
 TEST_F(OatDumpTest, TestNoDumpVmap) {
   std::string error_msg;
-  ASSERT_TRUE(Exec(kModeArt, {"--no-dump:vmap"}, /*list_only*/ false, &error_msg)) << error_msg;
+  ASSERT_TRUE(Exec(kDynamic, kModeArt, {"--no-dump:vmap"}, kListAndCode, &error_msg)) << error_msg;
+}
+TEST_F(OatDumpTest, TestNoDumpVmapStatic) {
+  TEST_DISABLED_FOR_NON_STATIC_HOST_BUILDS();
+  std::string error_msg;
+  ASSERT_TRUE(Exec(kStatic, kModeArt, {"--no-dump:vmap"}, kListAndCode, &error_msg)) << error_msg;
 }
 
 TEST_F(OatDumpTest, TestNoDisassemble) {
   std::string error_msg;
-  ASSERT_TRUE(Exec(kModeArt, {"--no-disassemble"}, /*list_only*/ false, &error_msg)) << error_msg;
+  ASSERT_TRUE(Exec(kDynamic, kModeArt, {"--no-disassemble"}, kListAndCode, &error_msg))
+      << error_msg;
+}
+TEST_F(OatDumpTest, TestNoDisassembleStatic) {
+  TEST_DISABLED_FOR_NON_STATIC_HOST_BUILDS();
+  std::string error_msg;
+  ASSERT_TRUE(Exec(kStatic, kModeArt, {"--no-disassemble"}, kListAndCode, &error_msg)) << error_msg;
 }
 
 TEST_F(OatDumpTest, TestListClasses) {
   std::string error_msg;
-  ASSERT_TRUE(Exec(kModeArt, {"--list-classes"}, /*list_only*/ true, &error_msg)) << error_msg;
+  ASSERT_TRUE(Exec(kDynamic, kModeArt, {"--list-classes"}, kListOnly, &error_msg)) << error_msg;
+}
+TEST_F(OatDumpTest, TestListClassesStatic) {
+  TEST_DISABLED_FOR_NON_STATIC_HOST_BUILDS();
+  std::string error_msg;
+  ASSERT_TRUE(Exec(kStatic, kModeArt, {"--list-classes"}, kListOnly, &error_msg)) << error_msg;
 }
 
 TEST_F(OatDumpTest, TestListMethods) {
   std::string error_msg;
-  ASSERT_TRUE(Exec(kModeArt, {"--list-methods"}, /*list_only*/ true, &error_msg)) << error_msg;
+  ASSERT_TRUE(Exec(kDynamic, kModeArt, {"--list-methods"}, kListOnly, &error_msg)) << error_msg;
+}
+TEST_F(OatDumpTest, TestListMethodsStatic) {
+  TEST_DISABLED_FOR_NON_STATIC_HOST_BUILDS();
+  std::string error_msg;
+  ASSERT_TRUE(Exec(kStatic, kModeArt, {"--list-methods"}, kListOnly, &error_msg)) << error_msg;
 }
 
 TEST_F(OatDumpTest, TestSymbolize) {
   std::string error_msg;
-  ASSERT_TRUE(Exec(kModeSymbolize, {}, /*list_only*/ true, &error_msg)) << error_msg;
+  ASSERT_TRUE(Exec(kDynamic, kModeSymbolize, {}, kListOnly, &error_msg)) << error_msg;
+}
+TEST_F(OatDumpTest, TestSymbolizeStatic) {
+  TEST_DISABLED_FOR_NON_STATIC_HOST_BUILDS();
+  std::string error_msg;
+  ASSERT_TRUE(Exec(kStatic, kModeSymbolize, {}, kListOnly, &error_msg)) << error_msg;
 }
 #endif
 }  // namespace art
diff --git a/runtime/common_runtime_test.h b/runtime/common_runtime_test.h
index 2d16a49..1542ebe 100644
--- a/runtime/common_runtime_test.h
+++ b/runtime/common_runtime_test.h
@@ -213,6 +213,12 @@
     return; \
   }
 
+#define TEST_DISABLED_FOR_NON_STATIC_HOST_BUILDS() \
+  if (!kHostStaticBuildEnabled) { \
+    printf("WARNING: TEST DISABLED FOR NON-STATIC HOST BUILDS\n"); \
+    return; \
+  }
+
 }  // namespace art
 
 namespace std {
diff --git a/runtime/globals.h b/runtime/globals.h
index 9045d40..aba5661 100644
--- a/runtime/globals.h
+++ b/runtime/globals.h
@@ -67,22 +67,30 @@
 #if defined(ART_TARGET)
 // Useful in conditionals where ART_TARGET isn't.
 static constexpr bool kIsTargetBuild = true;
-#if defined(ART_TARGET_LINUX)
+# if defined(ART_TARGET_LINUX)
 static constexpr bool kIsTargetLinux = true;
-#elif defined(ART_TARGET_ANDROID)
+# elif defined(ART_TARGET_ANDROID)
 static constexpr bool kIsTargetLinux = false;
-#else
-#error "Either ART_TARGET_LINUX or ART_TARGET_ANDROID needs to be defined for target builds."
-#endif
+# else
+# error "Either ART_TARGET_LINUX or ART_TARGET_ANDROID needs to be defined for target builds."
+# endif
 #else
 static constexpr bool kIsTargetBuild = false;
-#if defined(ART_TARGET_LINUX)
-#error "ART_TARGET_LINUX defined for host build."
-#elif defined(ART_TARGET_ANDROID)
-#error "ART_TARGET_ANDROID defined for host build."
-#else
+# if defined(ART_TARGET_LINUX)
+# error "ART_TARGET_LINUX defined for host build."
+# elif defined(ART_TARGET_ANDROID)
+# error "ART_TARGET_ANDROID defined for host build."
+# else
 static constexpr bool kIsTargetLinux = false;
+# endif
 #endif
+
+// Are additional statically-linked ART host binaries (dex2oats,
+// oatdumps, etc.) built and available?
+#if !defined(ART_TARGET) && defined(ART_BUILD_HOST_STATIC)
+static constexpr bool kHostStaticBuildEnabled = true;
+#else
+static constexpr bool kHostStaticBuildEnabled = false;
 #endif
 
 // Garbage collector constants.