Use ArchType in dexpreopt config

Make ArchType implement the encoding.TextMarshaller and
encoding.TextUnmarshaller interfaces so that it can be used
as a value in the dexpreopt config structs that are passed
through JSON files.

Test: m checkbuild
Change-Id: Ie4c12443e7ee5fe43f42d5403bcde12d62f617e2
diff --git a/android/arch.go b/android/arch.go
index ad812a4..b88b275 100644
--- a/android/arch.go
+++ b/android/arch.go
@@ -15,9 +15,11 @@
 package android
 
 import (
+	"encoding"
 	"fmt"
 	"reflect"
 	"runtime"
+	"strconv"
 	"strings"
 
 	"github.com/google/blueprint/proptools"
@@ -369,6 +371,23 @@
 	return a.Name
 }
 
+var _ encoding.TextMarshaler = ArchType{}
+
+func (a ArchType) MarshalText() ([]byte, error) {
+	return []byte(strconv.Quote(a.String())), nil
+}
+
+var _ encoding.TextUnmarshaler = &ArchType{}
+
+func (a *ArchType) UnmarshalText(text []byte) error {
+	if u, ok := archTypeMap[string(text)]; ok {
+		*a = u
+		return nil
+	}
+
+	return fmt.Errorf("unknown ArchType %q", text)
+}
+
 var BuildOs = func() OsType {
 	switch runtime.GOOS {
 	case "linux":
diff --git a/dexpreopt/config.go b/dexpreopt/config.go
index 064992f..8fef010 100644
--- a/dexpreopt/config.go
+++ b/dexpreopt/config.go
@@ -17,6 +17,8 @@
 import (
 	"encoding/json"
 	"io/ioutil"
+
+	"android/soong/android"
 )
 
 // GlobalConfig stores the configuration for dex preopting set by the product
@@ -66,9 +68,9 @@
 
 	EmptyDirectory string // path to an empty directory
 
-	DefaultDexPreoptImage  map[string]string // default boot image location for each architecture
-	CpuVariant             map[string]string // cpu variant for each architecture
-	InstructionSetFeatures map[string]string // instruction set for each architecture
+	DefaultDexPreoptImage  map[android.ArchType]string // default boot image location for each architecture
+	CpuVariant             map[android.ArchType]string // cpu variant for each architecture
+	InstructionSetFeatures map[android.ArchType]string // instruction set for each architecture
 
 	Tools Tools // paths to tools possibly used by the generated commands
 }
@@ -103,7 +105,7 @@
 	UsesLibraries         []string
 	LibraryPaths          map[string]string
 
-	Archs           []string
+	Archs           []android.ArchType
 	DexPreoptImages []string
 
 	PreoptExtractedApk bool // Overrides OnlyPreoptModules
diff --git a/dexpreopt/dexpreopt.go b/dexpreopt/dexpreopt.go
index 660a6d0..cd931df 100644
--- a/dexpreopt/dexpreopt.go
+++ b/dexpreopt/dexpreopt.go
@@ -115,7 +115,7 @@
 
 			for i, arch := range module.Archs {
 				image := module.DexPreoptImages[i]
-				dexpreoptCommand(global, module, rule, profile, arch, image, appImage, generateDM)
+				dexpreoptCommand(global, module, rule, arch, profile, image, appImage, generateDM)
 			}
 		}
 	}
@@ -178,7 +178,7 @@
 }
 
 func dexpreoptCommand(global GlobalConfig, module ModuleConfig, rule *android.RuleBuilder,
-	profile, arch, bootImage string, appImage, generateDM bool) {
+	arch android.ArchType, profile, bootImage string, appImage, generateDM bool) {
 
 	// HACK: make soname in Soong-generated .odex files match Make.
 	base := filepath.Base(module.DexLocation)
@@ -192,7 +192,7 @@
 		return filepath.Join(
 			filepath.Dir(path),
 			"oat",
-			arch,
+			arch.String(),
 			pathtools.ReplaceExtension(filepath.Base(path), "odex"))
 	}
 
@@ -328,7 +328,7 @@
 		FlagWithOutput("--oat-file=", odexPath).ImplicitOutput(vdexPath).
 		// Pass an empty directory, dex2oat shouldn't be reading arbitrary files
 		FlagWithArg("--android-root=", global.EmptyDirectory).
-		FlagWithArg("--instruction-set=", arch).
+		FlagWithArg("--instruction-set=", arch.String()).
 		FlagWithArg("--instruction-set-variant=", global.CpuVariant[arch]).
 		FlagWithArg("--instruction-set-features=", global.InstructionSetFeatures[arch]).
 		Flag("--no-generate-debug-info").
@@ -519,10 +519,10 @@
 }
 
 // PathToLocation converts .../system/framework/arm64/boot.art to .../system/framework/boot.art
-func PathToLocation(path, arch string) string {
+func PathToLocation(path string, arch android.ArchType) string {
 	pathArch := filepath.Base(filepath.Dir(path))
-	if pathArch != arch {
-		panic(fmt.Errorf("last directory in %q must be %q", path, arch))
+	if pathArch != arch.String() {
+		panic(fmt.Errorf("last directory in %q must be %q", path, arch.String()))
 	}
 	return filepath.Join(filepath.Dir(filepath.Dir(path)), filepath.Base(path))
 }
diff --git a/dexpreopt/dexpreopt_test.go b/dexpreopt/dexpreopt_test.go
index bcfd73c..be86190 100644
--- a/dexpreopt/dexpreopt_test.go
+++ b/dexpreopt/dexpreopt_test.go
@@ -76,7 +76,7 @@
 	OptionalUsesLibraries: nil,
 	UsesLibraries:         nil,
 	LibraryPaths:          nil,
-	Archs:                 []string{"arm"},
+	Archs:                 []android.ArchType{android.Arm},
 	DexPreoptImages:       []string{"system/framework/arm/boot.art"},
 	PreoptExtractedApk:    false,
 	NoCreateAppImage:      false,
diff --git a/java/dexpreopt.go b/java/dexpreopt.go
index eef51a9..cb6427b 100644
--- a/java/dexpreopt.go
+++ b/java/dexpreopt.go
@@ -100,14 +100,14 @@
 		return dexpreopt.GlobalConfig{}
 	}).(dexpreopt.GlobalConfig)
 
-	var archs []string
+	var archs []android.ArchType
 	for _, a := range ctx.MultiTargets() {
-		archs = append(archs, a.Arch.ArchType.String())
+		archs = append(archs, a.Arch.ArchType)
 	}
 	if len(archs) == 0 {
 		// assume this is a java library, dexpreopt for all arches for now
 		for _, target := range ctx.Config().Targets[android.Android] {
-			archs = append(archs, target.Arch.ArchType.String())
+			archs = append(archs, target.Arch.ArchType)
 		}
 		if inList(ctx.ModuleName(), globalConfig.SystemServerJars) && !d.isSDKLibrary {
 			// If the module is not an SDK library and it's a system server jar, only preopt the primary arch.