Write dexpreopt.config again during the build

Writing $OUT/dexpreopt.config during build setup and then using
it during the build causes bad incremental builds if a cleanspec
removes $OUT/dexpreopt.config.  Create a rule in Soong to write
out a copy so that it is a normal build artifact.

Bug: 132379813
Test: m checkbuild
Change-Id: I7755a7f8c1d6561fc83022a7f4cf7e4a64e989c9
diff --git a/dexpreopt/config.go b/dexpreopt/config.go
index 3b77042..1e0f862 100644
--- a/dexpreopt/config.go
+++ b/dexpreopt/config.go
@@ -176,7 +176,7 @@
 
 // LoadGlobalConfig reads the global dexpreopt.config file into a GlobalConfig struct.  It is used directly in Soong
 // and in dexpreopt_gen called from Make to read the $OUT/dexpreopt.config written by Make.
-func LoadGlobalConfig(ctx android.PathContext, path string) (GlobalConfig, error) {
+func LoadGlobalConfig(ctx android.PathContext, path string) (GlobalConfig, []byte, error) {
 	type GlobalJSONConfig struct {
 		GlobalConfig
 
@@ -199,9 +199,9 @@
 	}
 
 	config := GlobalJSONConfig{}
-	err := loadConfig(ctx, path, &config)
+	data, err := loadConfig(ctx, path, &config)
 	if err != nil {
-		return config.GlobalConfig, err
+		return config.GlobalConfig, nil, err
 	}
 
 	// Construct paths that require a PathContext.
@@ -217,7 +217,7 @@
 	config.GlobalConfig.Tools.VerifyUsesLibraries = constructPath(ctx, config.Tools.VerifyUsesLibraries)
 	config.GlobalConfig.Tools.ConstructContext = constructPath(ctx, config.Tools.ConstructContext)
 
-	return config.GlobalConfig, nil
+	return config.GlobalConfig, data, nil
 }
 
 // LoadModuleConfig reads a per-module dexpreopt.config file into a ModuleConfig struct.  It is not used in Soong, which
@@ -241,7 +241,7 @@
 
 	config := ModuleJSONConfig{}
 
-	err := loadConfig(ctx, path, &config)
+	_, err := loadConfig(ctx, path, &config)
 	if err != nil {
 		return config.ModuleConfig, err
 	}
@@ -259,24 +259,24 @@
 	return config.ModuleConfig, nil
 }
 
-func loadConfig(ctx android.PathContext, path string, config interface{}) error {
+func loadConfig(ctx android.PathContext, path string, config interface{}) ([]byte, error) {
 	r, err := ctx.Fs().Open(path)
 	if err != nil {
-		return err
+		return nil, err
 	}
 	defer r.Close()
 
 	data, err := ioutil.ReadAll(r)
 	if err != nil {
-		return err
+		return nil, err
 	}
 
 	err = json.Unmarshal(data, config)
 	if err != nil {
-		return err
+		return nil, err
 	}
 
-	return nil
+	return data, nil
 }
 
 func GlobalConfigForTests(ctx android.PathContext) GlobalConfig {
diff --git a/dexpreopt/dexpreopt_gen/dexpreopt_gen.go b/dexpreopt/dexpreopt_gen/dexpreopt_gen.go
index c72f684..d54ddb1 100644
--- a/dexpreopt/dexpreopt_gen/dexpreopt_gen.go
+++ b/dexpreopt/dexpreopt_gen/dexpreopt_gen.go
@@ -78,7 +78,7 @@
 
 	ctx := &pathContext{android.TestConfig(*outDir, nil)}
 
-	globalConfig, err := dexpreopt.LoadGlobalConfig(ctx, *globalConfigPath)
+	globalConfig, _, err := dexpreopt.LoadGlobalConfig(ctx, *globalConfigPath)
 	if err != nil {
 		fmt.Fprintf(os.Stderr, "error loading global config %q: %s\n", *globalConfigPath, err)
 		os.Exit(2)
diff --git a/java/dexpreopt_bootjars.go b/java/dexpreopt_bootjars.go
index f48428f..2a1a901 100644
--- a/java/dexpreopt_bootjars.go
+++ b/java/dexpreopt_bootjars.go
@@ -115,6 +115,8 @@
 type dexpreoptBootJars struct {
 	defaultBootImage *bootImage
 	otherImages      []*bootImage
+
+	dexpreoptConfigForMake android.WritablePath
 }
 
 // dexpreoptBoot singleton rules
@@ -123,6 +125,9 @@
 		return
 	}
 
+	d.dexpreoptConfigForMake = android.PathForOutput(ctx, ctx.Config().DeviceName(), "dexpreopt.config")
+	writeGlobalConfigForMake(ctx, d.dexpreoptConfigForMake)
+
 	global := dexpreoptGlobalConfig(ctx)
 
 	// Skip recompiling the boot image for the second sanitization phase. We'll get separate paths
@@ -453,8 +458,24 @@
 
 }
 
+func writeGlobalConfigForMake(ctx android.SingletonContext, path android.WritablePath) {
+	data := dexpreoptGlobalConfigRaw(ctx).data
+
+	ctx.Build(pctx, android.BuildParams{
+		Rule:   android.WriteFile,
+		Output: path,
+		Args: map[string]string{
+			"content": string(data),
+		},
+	})
+}
+
 // Export paths for default boot image to Make
 func (d *dexpreoptBootJars) MakeVars(ctx android.MakeVarsContext) {
+	if d.dexpreoptConfigForMake != nil {
+		ctx.Strict("DEX_PREOPT_CONFIG_FOR_MAKE", d.dexpreoptConfigForMake.String())
+	}
+
 	image := d.defaultBootImage
 	if image != nil {
 		ctx.Strict("DEXPREOPT_IMAGE_PROFILE_BUILT_INSTALLED", image.profileInstalls.String())
diff --git a/java/dexpreopt_config.go b/java/dexpreopt_config.go
index 270fcb4..d903f45 100644
--- a/java/dexpreopt_config.go
+++ b/java/dexpreopt_config.go
@@ -15,40 +15,50 @@
 package java
 
 import (
-	"android/soong/android"
-	"android/soong/dexpreopt"
 	"path/filepath"
 	"strings"
+
+	"android/soong/android"
+	"android/soong/dexpreopt"
 )
 
 // dexpreoptGlobalConfig returns the global dexpreopt.config.  It is loaded once the first time it is called for any
 // ctx.Config(), and returns the same data for all future calls with the same ctx.Config().  A value can be inserted
 // for tests using setDexpreoptTestGlobalConfig.
 func dexpreoptGlobalConfig(ctx android.PathContext) dexpreopt.GlobalConfig {
+	return dexpreoptGlobalConfigRaw(ctx).global
+}
+
+type globalConfigAndRaw struct {
+	global dexpreopt.GlobalConfig
+	data   []byte
+}
+
+func dexpreoptGlobalConfigRaw(ctx android.PathContext) globalConfigAndRaw {
 	return ctx.Config().Once(dexpreoptGlobalConfigKey, func() interface{} {
 		if f := ctx.Config().DexpreoptGlobalConfig(); f != "" {
 			ctx.AddNinjaFileDeps(f)
-			globalConfig, err := dexpreopt.LoadGlobalConfig(ctx, f)
+			globalConfig, data, err := dexpreopt.LoadGlobalConfig(ctx, f)
 			if err != nil {
 				panic(err)
 			}
-			return globalConfig
+			return globalConfigAndRaw{globalConfig, data}
 		}
 
 		// No global config filename set, see if there is a test config set
 		return ctx.Config().Once(dexpreoptTestGlobalConfigKey, func() interface{} {
 			// Nope, return a config with preopting disabled
-			return dexpreopt.GlobalConfig{
+			return globalConfigAndRaw{dexpreopt.GlobalConfig{
 				DisablePreopt: true,
-			}
+			}, nil}
 		})
-	}).(dexpreopt.GlobalConfig)
+	}).(globalConfigAndRaw)
 }
 
 // setDexpreoptTestGlobalConfig sets a GlobalConfig that future calls to dexpreoptGlobalConfig will return.  It must
 // be called before the first call to dexpreoptGlobalConfig for the config.
 func setDexpreoptTestGlobalConfig(config android.Config, globalConfig dexpreopt.GlobalConfig) {
-	config.Once(dexpreoptTestGlobalConfigKey, func() interface{} { return globalConfig })
+	config.Once(dexpreoptTestGlobalConfigKey, func() interface{} { return globalConfigAndRaw{globalConfig, nil} })
 }
 
 var dexpreoptGlobalConfigKey = android.NewOnceKey("DexpreoptGlobalConfig")