• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright 2018 Google Inc. All rights reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package main
16
17import (
18	"bytes"
19	"flag"
20	"fmt"
21	"os"
22	"path/filepath"
23	"runtime"
24	"strings"
25
26	"android/soong/android"
27	"android/soong/dexpreopt"
28
29	"github.com/google/blueprint/pathtools"
30)
31
32var (
33	dexpreoptScriptPath = flag.String("dexpreopt_script", "", "path to output dexpreopt script")
34	stripScriptPath     = flag.String("strip_script", "", "path to output strip script")
35	globalConfigPath    = flag.String("global", "", "path to global configuration file")
36	moduleConfigPath    = flag.String("module", "", "path to module configuration file")
37	outDir              = flag.String("out_dir", "", "path to output directory")
38)
39
40type pathContext struct {
41	config android.Config
42}
43
44func (x *pathContext) Fs() pathtools.FileSystem   { return pathtools.OsFs }
45func (x *pathContext) Config() android.Config     { return x.config }
46func (x *pathContext) AddNinjaFileDeps(...string) {}
47
48func main() {
49	flag.Parse()
50
51	usage := func(err string) {
52		if err != "" {
53			fmt.Println(err)
54			flag.Usage()
55			os.Exit(1)
56		}
57	}
58
59	if flag.NArg() > 0 {
60		usage("unrecognized argument " + flag.Arg(0))
61	}
62
63	if *dexpreoptScriptPath == "" {
64		usage("path to output dexpreopt script is required")
65	}
66
67	if *stripScriptPath == "" {
68		usage("path to output strip script is required")
69	}
70
71	if *globalConfigPath == "" {
72		usage("path to global configuration file is required")
73	}
74
75	if *moduleConfigPath == "" {
76		usage("path to module configuration file is required")
77	}
78
79	ctx := &pathContext{android.TestConfig(*outDir, nil)}
80
81	globalConfig, err := dexpreopt.LoadGlobalConfig(ctx, *globalConfigPath)
82	if err != nil {
83		fmt.Fprintf(os.Stderr, "error loading global config %q: %s\n", *globalConfigPath, err)
84		os.Exit(2)
85	}
86
87	moduleConfig, err := dexpreopt.LoadModuleConfig(ctx, *moduleConfigPath)
88	if err != nil {
89		fmt.Fprintf(os.Stderr, "error loading module config %q: %s\n", *moduleConfigPath, err)
90		os.Exit(2)
91	}
92
93	// This shouldn't be using *PathForTesting, but it's outside of soong_build so its OK for now.
94	moduleConfig.StripInputPath = android.PathForTesting("$1")
95	moduleConfig.StripOutputPath = android.WritablePathForTesting("$2")
96
97	moduleConfig.DexPath = android.PathForTesting("$1")
98
99	defer func() {
100		if r := recover(); r != nil {
101			switch x := r.(type) {
102			case runtime.Error:
103				panic(x)
104			case error:
105				fmt.Fprintln(os.Stderr, "error:", r)
106				os.Exit(3)
107			default:
108				panic(x)
109			}
110		}
111	}()
112
113	writeScripts(ctx, globalConfig, moduleConfig, *dexpreoptScriptPath, *stripScriptPath)
114}
115
116func writeScripts(ctx android.PathContext, global dexpreopt.GlobalConfig, module dexpreopt.ModuleConfig,
117	dexpreoptScriptPath, stripScriptPath string) {
118	dexpreoptRule, err := dexpreopt.GenerateDexpreoptRule(ctx, global, module)
119	if err != nil {
120		panic(err)
121	}
122
123	installDir := module.BuildPath.InSameDir(ctx, "dexpreopt_install")
124
125	dexpreoptRule.Command().FlagWithArg("rm -rf ", installDir.String())
126	dexpreoptRule.Command().FlagWithArg("mkdir -p ", installDir.String())
127
128	for _, install := range dexpreoptRule.Installs() {
129		installPath := installDir.Join(ctx, strings.TrimPrefix(install.To, "/"))
130		dexpreoptRule.Command().Text("mkdir -p").Flag(filepath.Dir(installPath.String()))
131		dexpreoptRule.Command().Text("cp -f").Input(install.From).Output(installPath)
132	}
133	dexpreoptRule.Command().Tool(global.Tools.SoongZip).
134		FlagWithArg("-o ", "$2").
135		FlagWithArg("-C ", installDir.String()).
136		FlagWithArg("-D ", installDir.String())
137
138	stripRule, err := dexpreopt.GenerateStripRule(global, module)
139	if err != nil {
140		panic(err)
141	}
142
143	write := func(rule *android.RuleBuilder, file string) {
144		script := &bytes.Buffer{}
145		script.WriteString(scriptHeader)
146		for _, c := range rule.Commands() {
147			script.WriteString(c)
148			script.WriteString("\n\n")
149		}
150
151		depFile := &bytes.Buffer{}
152
153		fmt.Fprint(depFile, `: \`+"\n")
154		for _, tool := range rule.Tools() {
155			fmt.Fprintf(depFile, `    %s \`+"\n", tool)
156		}
157		for _, input := range rule.Inputs() {
158			// Assume the rule that ran the script already has a dependency on the input file passed on the
159			// command line.
160			if input.String() != "$1" {
161				fmt.Fprintf(depFile, `    %s \`+"\n", input)
162			}
163		}
164		depFile.WriteString("\n")
165
166		fmt.Fprintln(script, "rm -f $2.d")
167		// Write the output path unescaped so the $2 gets expanded
168		fmt.Fprintln(script, `echo -n $2 > $2.d`)
169		// Write the rest of the depsfile using cat <<'EOF', which will not do any shell expansion on
170		// the contents to preserve backslashes and special characters in filenames.
171		fmt.Fprintf(script, "cat >> $2.d <<'EOF'\n%sEOF\n", depFile.String())
172
173		err := pathtools.WriteFileIfChanged(file, script.Bytes(), 0755)
174		if err != nil {
175			panic(err)
176		}
177	}
178
179	// The written scripts will assume the input is $1 and the output is $2
180	if module.DexPath.String() != "$1" {
181		panic(fmt.Errorf("module.DexPath must be '$1', was %q", module.DexPath))
182	}
183	if module.StripInputPath.String() != "$1" {
184		panic(fmt.Errorf("module.StripInputPath must be '$1', was %q", module.StripInputPath))
185	}
186	if module.StripOutputPath.String() != "$2" {
187		panic(fmt.Errorf("module.StripOutputPath must be '$2', was %q", module.StripOutputPath))
188	}
189
190	write(dexpreoptRule, dexpreoptScriptPath)
191	write(stripRule, stripScriptPath)
192}
193
194const scriptHeader = `#!/bin/bash
195
196err() {
197  errno=$?
198  echo "error: $0:$1 exited with status $errno" >&2
199  echo "error in command:" >&2
200  sed -n -e "$1p" $0 >&2
201  if [ "$errno" -ne 0 ]; then
202    exit $errno
203  else
204    exit 1
205  fi
206}
207
208trap 'err $LINENO' ERR
209
210`
211