xref: /aosp_15_r20/build/soong/dexpreopt/dexpreopt_gen/dexpreopt_gen.go (revision 333d2b3687b3a337dbcca9d65000bca186795e39)
1*333d2b36SAndroid Build Coastguard Worker// Copyright 2018 Google Inc. All rights reserved.
2*333d2b36SAndroid Build Coastguard Worker//
3*333d2b36SAndroid Build Coastguard Worker// Licensed under the Apache License, Version 2.0 (the "License");
4*333d2b36SAndroid Build Coastguard Worker// you may not use this file except in compliance with the License.
5*333d2b36SAndroid Build Coastguard Worker// You may obtain a copy of the License at
6*333d2b36SAndroid Build Coastguard Worker//
7*333d2b36SAndroid Build Coastguard Worker//     http://www.apache.org/licenses/LICENSE-2.0
8*333d2b36SAndroid Build Coastguard Worker//
9*333d2b36SAndroid Build Coastguard Worker// Unless required by applicable law or agreed to in writing, software
10*333d2b36SAndroid Build Coastguard Worker// distributed under the License is distributed on an "AS IS" BASIS,
11*333d2b36SAndroid Build Coastguard Worker// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12*333d2b36SAndroid Build Coastguard Worker// See the License for the specific language governing permissions and
13*333d2b36SAndroid Build Coastguard Worker// limitations under the License.
14*333d2b36SAndroid Build Coastguard Worker
15*333d2b36SAndroid Build Coastguard Workerpackage main
16*333d2b36SAndroid Build Coastguard Worker
17*333d2b36SAndroid Build Coastguard Workerimport (
18*333d2b36SAndroid Build Coastguard Worker	"bytes"
19*333d2b36SAndroid Build Coastguard Worker	"flag"
20*333d2b36SAndroid Build Coastguard Worker	"fmt"
21*333d2b36SAndroid Build Coastguard Worker	"io/ioutil"
22*333d2b36SAndroid Build Coastguard Worker	"os"
23*333d2b36SAndroid Build Coastguard Worker	"path/filepath"
24*333d2b36SAndroid Build Coastguard Worker	"runtime"
25*333d2b36SAndroid Build Coastguard Worker	"strings"
26*333d2b36SAndroid Build Coastguard Worker
27*333d2b36SAndroid Build Coastguard Worker	"android/soong/android"
28*333d2b36SAndroid Build Coastguard Worker	"android/soong/dexpreopt"
29*333d2b36SAndroid Build Coastguard Worker
30*333d2b36SAndroid Build Coastguard Worker	"github.com/google/blueprint"
31*333d2b36SAndroid Build Coastguard Worker	"github.com/google/blueprint/pathtools"
32*333d2b36SAndroid Build Coastguard Worker)
33*333d2b36SAndroid Build Coastguard Worker
34*333d2b36SAndroid Build Coastguard Workervar (
35*333d2b36SAndroid Build Coastguard Worker	dexpreoptScriptPath   = flag.String("dexpreopt_script", "", "path to output dexpreopt script")
36*333d2b36SAndroid Build Coastguard Worker	globalSoongConfigPath = flag.String("global_soong", "", "path to global configuration file for settings originating from Soong")
37*333d2b36SAndroid Build Coastguard Worker	globalConfigPath      = flag.String("global", "", "path to global configuration file")
38*333d2b36SAndroid Build Coastguard Worker	moduleConfigPath      = flag.String("module", "", "path to module configuration file")
39*333d2b36SAndroid Build Coastguard Worker	outDir                = flag.String("out_dir", "", "path to output directory")
40*333d2b36SAndroid Build Coastguard Worker	// If uses_target_files is true, dexpreopt_gen will be running on extracted target_files.zip files.
41*333d2b36SAndroid Build Coastguard Worker	// In this case, the tool replace output file path with $(basePath)/$(on-device file path).
42*333d2b36SAndroid Build Coastguard Worker	// The flag is useful when running dex2oat on system image and vendor image which are built separately.
43*333d2b36SAndroid Build Coastguard Worker	usesTargetFiles = flag.Bool("uses_target_files", false, "whether or not dexpreopt is running on target_files")
44*333d2b36SAndroid Build Coastguard Worker	// basePath indicates the path where target_files.zip is extracted.
45*333d2b36SAndroid Build Coastguard Worker	basePath            = flag.String("base_path", ".", "base path where images and tools are extracted")
46*333d2b36SAndroid Build Coastguard Worker	productPackagesPath = flag.String("product_packages", "", "path to product_packages.txt")
47*333d2b36SAndroid Build Coastguard Worker)
48*333d2b36SAndroid Build Coastguard Worker
49*333d2b36SAndroid Build Coastguard Workertype builderContext struct {
50*333d2b36SAndroid Build Coastguard Worker	config android.Config
51*333d2b36SAndroid Build Coastguard Worker}
52*333d2b36SAndroid Build Coastguard Worker
53*333d2b36SAndroid Build Coastguard Workerfunc (x *builderContext) Config() android.Config                            { return x.config }
54*333d2b36SAndroid Build Coastguard Workerfunc (x *builderContext) AddNinjaFileDeps(...string)                        {}
55*333d2b36SAndroid Build Coastguard Workerfunc (x *builderContext) Build(android.PackageContext, android.BuildParams) {}
56*333d2b36SAndroid Build Coastguard Workerfunc (x *builderContext) Rule(android.PackageContext, string, blueprint.RuleParams, ...string) blueprint.Rule {
57*333d2b36SAndroid Build Coastguard Worker	return nil
58*333d2b36SAndroid Build Coastguard Worker}
59*333d2b36SAndroid Build Coastguard Worker
60*333d2b36SAndroid Build Coastguard Workerfunc main() {
61*333d2b36SAndroid Build Coastguard Worker	flag.Parse()
62*333d2b36SAndroid Build Coastguard Worker
63*333d2b36SAndroid Build Coastguard Worker	usage := func(err string) {
64*333d2b36SAndroid Build Coastguard Worker		if err != "" {
65*333d2b36SAndroid Build Coastguard Worker			fmt.Println(err)
66*333d2b36SAndroid Build Coastguard Worker			flag.Usage()
67*333d2b36SAndroid Build Coastguard Worker			os.Exit(1)
68*333d2b36SAndroid Build Coastguard Worker		}
69*333d2b36SAndroid Build Coastguard Worker	}
70*333d2b36SAndroid Build Coastguard Worker
71*333d2b36SAndroid Build Coastguard Worker	if flag.NArg() > 0 {
72*333d2b36SAndroid Build Coastguard Worker		usage("unrecognized argument " + flag.Arg(0))
73*333d2b36SAndroid Build Coastguard Worker	}
74*333d2b36SAndroid Build Coastguard Worker
75*333d2b36SAndroid Build Coastguard Worker	if *dexpreoptScriptPath == "" {
76*333d2b36SAndroid Build Coastguard Worker		usage("path to output dexpreopt script is required")
77*333d2b36SAndroid Build Coastguard Worker	}
78*333d2b36SAndroid Build Coastguard Worker
79*333d2b36SAndroid Build Coastguard Worker	if *globalSoongConfigPath == "" {
80*333d2b36SAndroid Build Coastguard Worker		usage("--global_soong configuration file is required")
81*333d2b36SAndroid Build Coastguard Worker	}
82*333d2b36SAndroid Build Coastguard Worker
83*333d2b36SAndroid Build Coastguard Worker	if *globalConfigPath == "" {
84*333d2b36SAndroid Build Coastguard Worker		usage("--global configuration file is required")
85*333d2b36SAndroid Build Coastguard Worker	}
86*333d2b36SAndroid Build Coastguard Worker
87*333d2b36SAndroid Build Coastguard Worker	if *moduleConfigPath == "" {
88*333d2b36SAndroid Build Coastguard Worker		usage("--module configuration file is required")
89*333d2b36SAndroid Build Coastguard Worker	}
90*333d2b36SAndroid Build Coastguard Worker
91*333d2b36SAndroid Build Coastguard Worker	if *productPackagesPath == "" {
92*333d2b36SAndroid Build Coastguard Worker		usage("--product_packages configuration file is required")
93*333d2b36SAndroid Build Coastguard Worker	}
94*333d2b36SAndroid Build Coastguard Worker
95*333d2b36SAndroid Build Coastguard Worker	// NOTE: duplicating --out_dir here is incorrect (one should be the another
96*333d2b36SAndroid Build Coastguard Worker	// plus "/soong" but doing so apparently breaks dexpreopt
97*333d2b36SAndroid Build Coastguard Worker	ctx := &builderContext{android.NullConfig(*outDir, *outDir)}
98*333d2b36SAndroid Build Coastguard Worker
99*333d2b36SAndroid Build Coastguard Worker	globalSoongConfigData, err := ioutil.ReadFile(*globalSoongConfigPath)
100*333d2b36SAndroid Build Coastguard Worker	if err != nil {
101*333d2b36SAndroid Build Coastguard Worker		fmt.Fprintf(os.Stderr, "error reading global Soong config %q: %s\n", *globalSoongConfigPath, err)
102*333d2b36SAndroid Build Coastguard Worker		os.Exit(2)
103*333d2b36SAndroid Build Coastguard Worker	}
104*333d2b36SAndroid Build Coastguard Worker
105*333d2b36SAndroid Build Coastguard Worker	globalSoongConfig, err := dexpreopt.ParseGlobalSoongConfig(ctx, globalSoongConfigData)
106*333d2b36SAndroid Build Coastguard Worker	if err != nil {
107*333d2b36SAndroid Build Coastguard Worker		fmt.Fprintf(os.Stderr, "error parsing global Soong config %q: %s\n", *globalSoongConfigPath, err)
108*333d2b36SAndroid Build Coastguard Worker		os.Exit(2)
109*333d2b36SAndroid Build Coastguard Worker	}
110*333d2b36SAndroid Build Coastguard Worker
111*333d2b36SAndroid Build Coastguard Worker	globalConfigData, err := ioutil.ReadFile(*globalConfigPath)
112*333d2b36SAndroid Build Coastguard Worker	if err != nil {
113*333d2b36SAndroid Build Coastguard Worker		fmt.Fprintf(os.Stderr, "error reading global config %q: %s\n", *globalConfigPath, err)
114*333d2b36SAndroid Build Coastguard Worker		os.Exit(2)
115*333d2b36SAndroid Build Coastguard Worker	}
116*333d2b36SAndroid Build Coastguard Worker
117*333d2b36SAndroid Build Coastguard Worker	globalConfig, err := dexpreopt.ParseGlobalConfig(ctx, globalConfigData)
118*333d2b36SAndroid Build Coastguard Worker	if err != nil {
119*333d2b36SAndroid Build Coastguard Worker		fmt.Fprintf(os.Stderr, "error parsing global config %q: %s\n", *globalConfigPath, err)
120*333d2b36SAndroid Build Coastguard Worker		os.Exit(2)
121*333d2b36SAndroid Build Coastguard Worker	}
122*333d2b36SAndroid Build Coastguard Worker
123*333d2b36SAndroid Build Coastguard Worker	moduleConfigData, err := ioutil.ReadFile(*moduleConfigPath)
124*333d2b36SAndroid Build Coastguard Worker	if err != nil {
125*333d2b36SAndroid Build Coastguard Worker		fmt.Fprintf(os.Stderr, "error reading module config %q: %s\n", *moduleConfigPath, err)
126*333d2b36SAndroid Build Coastguard Worker		os.Exit(2)
127*333d2b36SAndroid Build Coastguard Worker	}
128*333d2b36SAndroid Build Coastguard Worker
129*333d2b36SAndroid Build Coastguard Worker	moduleConfig, err := dexpreopt.ParseModuleConfig(ctx, moduleConfigData)
130*333d2b36SAndroid Build Coastguard Worker	if err != nil {
131*333d2b36SAndroid Build Coastguard Worker		fmt.Fprintf(os.Stderr, "error parsing module config %q: %s\n", *moduleConfigPath, err)
132*333d2b36SAndroid Build Coastguard Worker		os.Exit(2)
133*333d2b36SAndroid Build Coastguard Worker	}
134*333d2b36SAndroid Build Coastguard Worker
135*333d2b36SAndroid Build Coastguard Worker	moduleConfig.DexPath = android.PathForTesting("$1")
136*333d2b36SAndroid Build Coastguard Worker
137*333d2b36SAndroid Build Coastguard Worker	defer func() {
138*333d2b36SAndroid Build Coastguard Worker		if r := recover(); r != nil {
139*333d2b36SAndroid Build Coastguard Worker			switch x := r.(type) {
140*333d2b36SAndroid Build Coastguard Worker			case runtime.Error:
141*333d2b36SAndroid Build Coastguard Worker				panic(x)
142*333d2b36SAndroid Build Coastguard Worker			case error:
143*333d2b36SAndroid Build Coastguard Worker				fmt.Fprintln(os.Stderr, "error:", r)
144*333d2b36SAndroid Build Coastguard Worker				os.Exit(3)
145*333d2b36SAndroid Build Coastguard Worker			default:
146*333d2b36SAndroid Build Coastguard Worker				panic(x)
147*333d2b36SAndroid Build Coastguard Worker			}
148*333d2b36SAndroid Build Coastguard Worker		}
149*333d2b36SAndroid Build Coastguard Worker	}()
150*333d2b36SAndroid Build Coastguard Worker	if *usesTargetFiles {
151*333d2b36SAndroid Build Coastguard Worker		moduleConfig.ManifestPath = android.OptionalPath{}
152*333d2b36SAndroid Build Coastguard Worker		prefix := "dex2oat_result"
153*333d2b36SAndroid Build Coastguard Worker		moduleConfig.BuildPath = android.PathForOutput(ctx, filepath.Join(prefix, moduleConfig.DexLocation))
154*333d2b36SAndroid Build Coastguard Worker		for i, location := range moduleConfig.PreoptBootClassPathDexLocations {
155*333d2b36SAndroid Build Coastguard Worker			moduleConfig.PreoptBootClassPathDexFiles[i] = android.PathForSource(ctx, *basePath+location)
156*333d2b36SAndroid Build Coastguard Worker		}
157*333d2b36SAndroid Build Coastguard Worker		for i := range moduleConfig.ClassLoaderContexts {
158*333d2b36SAndroid Build Coastguard Worker			for _, v := range moduleConfig.ClassLoaderContexts[i] {
159*333d2b36SAndroid Build Coastguard Worker				v.Host = android.PathForSource(ctx, *basePath+v.Device)
160*333d2b36SAndroid Build Coastguard Worker			}
161*333d2b36SAndroid Build Coastguard Worker		}
162*333d2b36SAndroid Build Coastguard Worker		moduleConfig.EnforceUsesLibraries = false
163*333d2b36SAndroid Build Coastguard Worker		for i, location := range moduleConfig.DexPreoptImageLocationsOnDevice {
164*333d2b36SAndroid Build Coastguard Worker			moduleConfig.DexPreoptImageLocationsOnHost[i] = *basePath + location
165*333d2b36SAndroid Build Coastguard Worker		}
166*333d2b36SAndroid Build Coastguard Worker	}
167*333d2b36SAndroid Build Coastguard Worker	writeScripts(ctx, globalSoongConfig, globalConfig, moduleConfig, *dexpreoptScriptPath, *productPackagesPath)
168*333d2b36SAndroid Build Coastguard Worker}
169*333d2b36SAndroid Build Coastguard Worker
170*333d2b36SAndroid Build Coastguard Workerfunc writeScripts(ctx android.BuilderContext, globalSoong *dexpreopt.GlobalSoongConfig,
171*333d2b36SAndroid Build Coastguard Worker	global *dexpreopt.GlobalConfig, module *dexpreopt.ModuleConfig, dexpreoptScriptPath string,
172*333d2b36SAndroid Build Coastguard Worker	productPackagesPath string) {
173*333d2b36SAndroid Build Coastguard Worker	write := func(rule *android.RuleBuilder, file string) {
174*333d2b36SAndroid Build Coastguard Worker		script := &bytes.Buffer{}
175*333d2b36SAndroid Build Coastguard Worker		script.WriteString(scriptHeader)
176*333d2b36SAndroid Build Coastguard Worker		for _, c := range rule.Commands() {
177*333d2b36SAndroid Build Coastguard Worker			script.WriteString(c)
178*333d2b36SAndroid Build Coastguard Worker			script.WriteString("\n\n")
179*333d2b36SAndroid Build Coastguard Worker		}
180*333d2b36SAndroid Build Coastguard Worker
181*333d2b36SAndroid Build Coastguard Worker		depFile := &bytes.Buffer{}
182*333d2b36SAndroid Build Coastguard Worker
183*333d2b36SAndroid Build Coastguard Worker		fmt.Fprint(depFile, `: \`+"\n")
184*333d2b36SAndroid Build Coastguard Worker		for _, tool := range rule.Tools() {
185*333d2b36SAndroid Build Coastguard Worker			fmt.Fprintf(depFile, `    %s \`+"\n", tool)
186*333d2b36SAndroid Build Coastguard Worker		}
187*333d2b36SAndroid Build Coastguard Worker		for _, input := range rule.Inputs() {
188*333d2b36SAndroid Build Coastguard Worker			// Assume the rule that ran the script already has a dependency on the input file passed on the
189*333d2b36SAndroid Build Coastguard Worker			// command line.
190*333d2b36SAndroid Build Coastguard Worker			if input.String() != "$1" {
191*333d2b36SAndroid Build Coastguard Worker				fmt.Fprintf(depFile, `    %s \`+"\n", input)
192*333d2b36SAndroid Build Coastguard Worker			}
193*333d2b36SAndroid Build Coastguard Worker		}
194*333d2b36SAndroid Build Coastguard Worker		depFile.WriteString("\n")
195*333d2b36SAndroid Build Coastguard Worker
196*333d2b36SAndroid Build Coastguard Worker		fmt.Fprintln(script, "rm -f $2.d")
197*333d2b36SAndroid Build Coastguard Worker		// Write the output path unescaped so the $2 gets expanded
198*333d2b36SAndroid Build Coastguard Worker		fmt.Fprintln(script, `echo -n $2 > $2.d`)
199*333d2b36SAndroid Build Coastguard Worker		// Write the rest of the depsfile using cat <<'EOF', which will not do any shell expansion on
200*333d2b36SAndroid Build Coastguard Worker		// the contents to preserve backslashes and special characters in filenames.
201*333d2b36SAndroid Build Coastguard Worker		fmt.Fprintf(script, "cat >> $2.d <<'EOF'\n%sEOF\n", depFile.String())
202*333d2b36SAndroid Build Coastguard Worker
203*333d2b36SAndroid Build Coastguard Worker		err := pathtools.WriteFileIfChanged(file, script.Bytes(), 0755)
204*333d2b36SAndroid Build Coastguard Worker		if err != nil {
205*333d2b36SAndroid Build Coastguard Worker			panic(err)
206*333d2b36SAndroid Build Coastguard Worker		}
207*333d2b36SAndroid Build Coastguard Worker	}
208*333d2b36SAndroid Build Coastguard Worker	cpApexSscpServerJar := false // dexpreopt_gen operates on make modules, and since sscp libraries are in soong, this should be a noop
209*333d2b36SAndroid Build Coastguard Worker	dexpreoptRule, err := dexpreopt.GenerateDexpreoptRule(
210*333d2b36SAndroid Build Coastguard Worker		ctx, globalSoong, global, module, android.PathForTesting(productPackagesPath), cpApexSscpServerJar)
211*333d2b36SAndroid Build Coastguard Worker	if err != nil {
212*333d2b36SAndroid Build Coastguard Worker		panic(err)
213*333d2b36SAndroid Build Coastguard Worker	}
214*333d2b36SAndroid Build Coastguard Worker	// When usesTargetFiles is true, only odex/vdex files are necessary.
215*333d2b36SAndroid Build Coastguard Worker	// So skip redunant processes(such as copying the result to the artifact path, and zipping, and so on.)
216*333d2b36SAndroid Build Coastguard Worker	if *usesTargetFiles {
217*333d2b36SAndroid Build Coastguard Worker		write(dexpreoptRule, dexpreoptScriptPath)
218*333d2b36SAndroid Build Coastguard Worker		return
219*333d2b36SAndroid Build Coastguard Worker	}
220*333d2b36SAndroid Build Coastguard Worker	installDir := module.BuildPath.InSameDir(ctx, "dexpreopt_install")
221*333d2b36SAndroid Build Coastguard Worker
222*333d2b36SAndroid Build Coastguard Worker	dexpreoptRule.Command().FlagWithArg("rm -rf ", installDir.String())
223*333d2b36SAndroid Build Coastguard Worker	dexpreoptRule.Command().FlagWithArg("mkdir -p ", installDir.String())
224*333d2b36SAndroid Build Coastguard Worker
225*333d2b36SAndroid Build Coastguard Worker	for _, install := range dexpreoptRule.Installs() {
226*333d2b36SAndroid Build Coastguard Worker		installPath := installDir.Join(ctx, strings.TrimPrefix(install.To, "/"))
227*333d2b36SAndroid Build Coastguard Worker		dexpreoptRule.Command().Text("mkdir -p").Flag(filepath.Dir(installPath.String()))
228*333d2b36SAndroid Build Coastguard Worker		dexpreoptRule.Command().Text("cp -f").Input(install.From).Output(installPath)
229*333d2b36SAndroid Build Coastguard Worker	}
230*333d2b36SAndroid Build Coastguard Worker	dexpreoptRule.Command().Tool(globalSoong.SoongZip).
231*333d2b36SAndroid Build Coastguard Worker		FlagWithArg("-o ", "$2").
232*333d2b36SAndroid Build Coastguard Worker		FlagWithArg("-C ", installDir.String()).
233*333d2b36SAndroid Build Coastguard Worker		FlagWithArg("-D ", installDir.String())
234*333d2b36SAndroid Build Coastguard Worker
235*333d2b36SAndroid Build Coastguard Worker	// The written scripts will assume the input is $1 and the output is $2
236*333d2b36SAndroid Build Coastguard Worker	if module.DexPath.String() != "$1" {
237*333d2b36SAndroid Build Coastguard Worker		panic(fmt.Errorf("module.DexPath must be '$1', was %q", module.DexPath))
238*333d2b36SAndroid Build Coastguard Worker	}
239*333d2b36SAndroid Build Coastguard Worker
240*333d2b36SAndroid Build Coastguard Worker	write(dexpreoptRule, dexpreoptScriptPath)
241*333d2b36SAndroid Build Coastguard Worker}
242*333d2b36SAndroid Build Coastguard Worker
243*333d2b36SAndroid Build Coastguard Workerconst scriptHeader = `#!/bin/bash
244*333d2b36SAndroid Build Coastguard Worker
245*333d2b36SAndroid Build Coastguard Workererr() {
246*333d2b36SAndroid Build Coastguard Worker  errno=$?
247*333d2b36SAndroid Build Coastguard Worker  echo "error: $0:$1 exited with status $errno" >&2
248*333d2b36SAndroid Build Coastguard Worker  echo "error in command:" >&2
249*333d2b36SAndroid Build Coastguard Worker  sed -n -e "$1p" $0 >&2
250*333d2b36SAndroid Build Coastguard Worker  if [ "$errno" -ne 0 ]; then
251*333d2b36SAndroid Build Coastguard Worker    exit $errno
252*333d2b36SAndroid Build Coastguard Worker  else
253*333d2b36SAndroid Build Coastguard Worker    exit 1
254*333d2b36SAndroid Build Coastguard Worker  fi
255*333d2b36SAndroid Build Coastguard Worker}
256*333d2b36SAndroid Build Coastguard Worker
257*333d2b36SAndroid Build Coastguard Workertrap 'err $LINENO' ERR
258*333d2b36SAndroid Build Coastguard Worker
259*333d2b36SAndroid Build Coastguard Worker`
260