Add usesTargetFiles option in dexpreopt_gen

For running dex2oat on the target_files, the paths should be use the
device install path instead of the path starting with $(OUT).
So add usesTargetFiles option and basePath option which indicates
extracted path. With those options, the path is replaced with
$(basePath)/$(device path)

And also, add DexPreoptImageDeviceLocations in the config which refers
to the boot image path(without arch) on the device. Because
DexPreoptImage related device path was missing.

Bug: 158843648
Test: dexpreopt_gen -usesTargetFiles -basePath (extract path) and then
check if paths in the generated shell script are based on on-device
path.

Change-Id: I9667fadbf3b7c6f770e0d1bcbee5d67c1ecd8a3d
diff --git a/dexpreopt/config.go b/dexpreopt/config.go
index 1844bce..7e73bf7 100644
--- a/dexpreopt/config.go
+++ b/dexpreopt/config.go
@@ -130,9 +130,11 @@
 	ProvidesUsesLibrary            string       // library name (usually the same as module name)
 	ClassLoaderContexts            ClassLoaderContextMap
 
-	Archs                         []android.ArchType
-	DexPreoptImagesDeps           []android.OutputPaths
-	DexPreoptImageLocationsOnHost []string // boot image location on host (file path without the arch subdirectory)
+	Archs               []android.ArchType
+	DexPreoptImagesDeps []android.OutputPaths
+
+	DexPreoptImageLocationsOnHost   []string // boot image location on host (file path without the arch subdirectory)
+	DexPreoptImageLocationsOnDevice []string // boot image location on device (file path without the arch subdirectory)
 
 	PreoptBootClassPathDexFiles     android.Paths // file paths of boot class path files
 	PreoptBootClassPathDexLocations []string      // virtual locations of boot class path files
diff --git a/dexpreopt/dexpreopt.go b/dexpreopt/dexpreopt.go
index 9d9234f..da015a3 100644
--- a/dexpreopt/dexpreopt.go
+++ b/dexpreopt/dexpreopt.go
@@ -499,11 +499,16 @@
 
 // PathToLocation converts .../system/framework/arm64/boot.art to .../system/framework/boot.art
 func PathToLocation(path android.Path, arch android.ArchType) string {
-	pathArch := filepath.Base(filepath.Dir(path.String()))
+	return PathStringToLocation(path.String(), arch)
+}
+
+// PathStringToLocation converts .../system/framework/arm64/boot.art to .../system/framework/boot.art
+func PathStringToLocation(path string, arch android.ArchType) string {
+	pathArch := filepath.Base(filepath.Dir(path))
 	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.String())), filepath.Base(path.String()))
+	return filepath.Join(filepath.Dir(filepath.Dir(path)), filepath.Base(path))
 }
 
 func makefileMatch(pattern, s string) bool {
diff --git a/dexpreopt/dexpreopt_gen/dexpreopt_gen.go b/dexpreopt/dexpreopt_gen/dexpreopt_gen.go
index 32c4f84..7dbe74c 100644
--- a/dexpreopt/dexpreopt_gen/dexpreopt_gen.go
+++ b/dexpreopt/dexpreopt_gen/dexpreopt_gen.go
@@ -37,6 +37,12 @@
 	globalConfigPath      = flag.String("global", "", "path to global configuration file")
 	moduleConfigPath      = flag.String("module", "", "path to module configuration file")
 	outDir                = flag.String("out_dir", "", "path to output directory")
+	// If uses_target_files is true, dexpreopt_gen will be running on extracted target_files.zip files.
+	// In this case, the tool replace output file path with $(basePath)/$(on-device file path).
+	// The flag is useful when running dex2oat on system image and vendor image which are built separately.
+	usesTargetFiles = flag.Bool("uses_target_files", false, "whether or not dexpreopt is running on target_files")
+	// basePath indicates the path where target_files.zip is extracted.
+	basePath = flag.String("base_path", ".", "base path where images and tools are extracted")
 )
 
 type builderContext struct {
@@ -134,32 +140,28 @@
 			}
 		}
 	}()
-
+	if *usesTargetFiles {
+		moduleConfig.ManifestPath = android.OptionalPath{}
+		prefix := "dex2oat_result"
+		moduleConfig.BuildPath = android.PathForOutput(ctx, filepath.Join(prefix, moduleConfig.DexLocation))
+		for i, location := range moduleConfig.PreoptBootClassPathDexLocations {
+			moduleConfig.PreoptBootClassPathDexFiles[i] = android.PathForSource(ctx, *basePath+location)
+		}
+		for i := range moduleConfig.ClassLoaderContexts {
+			for _, v := range moduleConfig.ClassLoaderContexts[i] {
+				v.Host = android.PathForSource(ctx, *basePath+v.Device)
+			}
+		}
+		moduleConfig.EnforceUsesLibraries = false
+		for i, location := range moduleConfig.DexPreoptImageLocationsOnDevice {
+			moduleConfig.DexPreoptImageLocationsOnHost[i] = *basePath + location
+		}
+	}
 	writeScripts(ctx, globalSoongConfig, globalConfig, moduleConfig, *dexpreoptScriptPath)
 }
 
 func writeScripts(ctx android.BuilderContext, globalSoong *dexpreopt.GlobalSoongConfig,
 	global *dexpreopt.GlobalConfig, module *dexpreopt.ModuleConfig, dexpreoptScriptPath string) {
-	dexpreoptRule, err := dexpreopt.GenerateDexpreoptRule(ctx, globalSoong, global, module)
-	if err != nil {
-		panic(err)
-	}
-
-	installDir := module.BuildPath.InSameDir(ctx, "dexpreopt_install")
-
-	dexpreoptRule.Command().FlagWithArg("rm -rf ", installDir.String())
-	dexpreoptRule.Command().FlagWithArg("mkdir -p ", installDir.String())
-
-	for _, install := range dexpreoptRule.Installs() {
-		installPath := installDir.Join(ctx, strings.TrimPrefix(install.To, "/"))
-		dexpreoptRule.Command().Text("mkdir -p").Flag(filepath.Dir(installPath.String()))
-		dexpreoptRule.Command().Text("cp -f").Input(install.From).Output(installPath)
-	}
-	dexpreoptRule.Command().Tool(globalSoong.SoongZip).
-		FlagWithArg("-o ", "$2").
-		FlagWithArg("-C ", installDir.String()).
-		FlagWithArg("-D ", installDir.String())
-
 	write := func(rule *android.RuleBuilder, file string) {
 		script := &bytes.Buffer{}
 		script.WriteString(scriptHeader)
@@ -195,6 +197,30 @@
 			panic(err)
 		}
 	}
+	dexpreoptRule, err := dexpreopt.GenerateDexpreoptRule(ctx, globalSoong, global, module)
+	if err != nil {
+		panic(err)
+	}
+	// When usesTargetFiles is true, only odex/vdex files are necessary.
+	// So skip redunant processes(such as copying the result to the artifact path, and zipping, and so on.)
+	if *usesTargetFiles {
+		write(dexpreoptRule, dexpreoptScriptPath)
+		return
+	}
+	installDir := module.BuildPath.InSameDir(ctx, "dexpreopt_install")
+
+	dexpreoptRule.Command().FlagWithArg("rm -rf ", installDir.String())
+	dexpreoptRule.Command().FlagWithArg("mkdir -p ", installDir.String())
+
+	for _, install := range dexpreoptRule.Installs() {
+		installPath := installDir.Join(ctx, strings.TrimPrefix(install.To, "/"))
+		dexpreoptRule.Command().Text("mkdir -p").Flag(filepath.Dir(installPath.String()))
+		dexpreoptRule.Command().Text("cp -f").Input(install.From).Output(installPath)
+	}
+	dexpreoptRule.Command().Tool(globalSoong.SoongZip).
+		FlagWithArg("-o ", "$2").
+		FlagWithArg("-C ", installDir.String()).
+		FlagWithArg("-D ", installDir.String())
 
 	// The written scripts will assume the input is $1 and the output is $2
 	if module.DexPath.String() != "$1" {