blob: f9473489812ade7e3c65dfc6f324764128a90ee7 [file] [log] [blame]
Colin Crossfeec25b2019-01-30 17:32:39 -08001// Copyright 2019 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 android
16
17import (
Colin Cross758290d2019-02-01 16:42:32 -080018 "fmt"
Colin Crossfeec25b2019-01-30 17:32:39 -080019 "io/ioutil"
20 "os"
21 "path/filepath"
22 "reflect"
Colin Cross758290d2019-02-01 16:42:32 -080023 "strings"
Colin Crossfeec25b2019-01-30 17:32:39 -080024 "testing"
25)
26
Colin Cross758290d2019-02-01 16:42:32 -080027func ExampleRuleBuilder() {
28 rule := NewRuleBuilder()
29
Colin Crossab898dc2019-02-21 05:03:00 +000030 rule.Command().Tool("ld").Inputs([]string{"a.o", "b.o"}).FlagWithOutput("-o ", "linked")
Colin Cross758290d2019-02-01 16:42:32 -080031 rule.Command().Text("echo success")
32
33 // To add the command to the build graph:
34 // rule.Build(pctx, ctx, "link", "link")
35
36 fmt.Printf("commands: %q\n", strings.Join(rule.Commands(), " && "))
37 fmt.Printf("tools: %q\n", rule.Tools())
38 fmt.Printf("inputs: %q\n", rule.Inputs())
39 fmt.Printf("outputs: %q\n", rule.Outputs())
40
41 // Output:
Colin Crossab898dc2019-02-21 05:03:00 +000042 // commands: "ld a.o b.o -o linked && echo success"
Colin Cross758290d2019-02-01 16:42:32 -080043 // tools: ["ld"]
44 // inputs: ["a.o" "b.o"]
Colin Crossab898dc2019-02-21 05:03:00 +000045 // outputs: ["linked"]
Colin Cross758290d2019-02-01 16:42:32 -080046}
47
Colin Cross5cb5b092019-02-02 21:25:18 -080048func ExampleRuleBuilder_Temporary() {
49 rule := NewRuleBuilder()
50
Colin Crossab898dc2019-02-21 05:03:00 +000051 rule.Command().Tool("cp").Input("a").Output("b")
52 rule.Command().Tool("cp").Input("b").Output("c")
53 rule.Temporary("b")
Colin Cross5cb5b092019-02-02 21:25:18 -080054
55 fmt.Printf("commands: %q\n", strings.Join(rule.Commands(), " && "))
56 fmt.Printf("tools: %q\n", rule.Tools())
57 fmt.Printf("inputs: %q\n", rule.Inputs())
58 fmt.Printf("outputs: %q\n", rule.Outputs())
59
60 // Output:
Colin Crossab898dc2019-02-21 05:03:00 +000061 // commands: "cp a b && cp b c"
Colin Cross5cb5b092019-02-02 21:25:18 -080062 // tools: ["cp"]
63 // inputs: ["a"]
Colin Crossab898dc2019-02-21 05:03:00 +000064 // outputs: ["c"]
Colin Cross5cb5b092019-02-02 21:25:18 -080065}
66
67func ExampleRuleBuilder_DeleteTemporaryFiles() {
68 rule := NewRuleBuilder()
69
Colin Crossab898dc2019-02-21 05:03:00 +000070 rule.Command().Tool("cp").Input("a").Output("b")
71 rule.Command().Tool("cp").Input("b").Output("c")
72 rule.Temporary("b")
Colin Cross5cb5b092019-02-02 21:25:18 -080073 rule.DeleteTemporaryFiles()
74
75 fmt.Printf("commands: %q\n", strings.Join(rule.Commands(), " && "))
76 fmt.Printf("tools: %q\n", rule.Tools())
77 fmt.Printf("inputs: %q\n", rule.Inputs())
78 fmt.Printf("outputs: %q\n", rule.Outputs())
79
80 // Output:
Colin Crossab898dc2019-02-21 05:03:00 +000081 // commands: "cp a b && cp b c && rm -f b"
Colin Cross5cb5b092019-02-02 21:25:18 -080082 // tools: ["cp"]
83 // inputs: ["a"]
Colin Crossab898dc2019-02-21 05:03:00 +000084 // outputs: ["c"]
Colin Cross5cb5b092019-02-02 21:25:18 -080085}
86
Colin Crossdeabb942019-02-11 14:11:09 -080087func ExampleRuleBuilder_Installs() {
88 rule := NewRuleBuilder()
89
Colin Crossab898dc2019-02-21 05:03:00 +000090 rule.Command().Tool("ld").Inputs([]string{"a.o", "b.o"}).FlagWithOutput("-o ", "linked")
91 rule.Install("linked", "/bin/linked")
92 rule.Install("linked", "/sbin/linked")
Colin Crossdeabb942019-02-11 14:11:09 -080093
94 fmt.Printf("rule.Installs().String() = %q\n", rule.Installs().String())
95
96 // Output:
Colin Crossab898dc2019-02-21 05:03:00 +000097 // rule.Installs().String() = "linked:/bin/linked linked:/sbin/linked"
Colin Crossdeabb942019-02-11 14:11:09 -080098}
99
Colin Cross758290d2019-02-01 16:42:32 -0800100func ExampleRuleBuilderCommand() {
101 rule := NewRuleBuilder()
102
103 // chained
Colin Crossab898dc2019-02-21 05:03:00 +0000104 rule.Command().Tool("ld").Inputs([]string{"a.o", "b.o"}).FlagWithOutput("-o ", "linked")
Colin Cross758290d2019-02-01 16:42:32 -0800105
106 // unchained
107 cmd := rule.Command()
Colin Crossab898dc2019-02-21 05:03:00 +0000108 cmd.Tool("ld")
109 cmd.Inputs([]string{"a.o", "b.o"})
110 cmd.FlagWithOutput("-o ", "linked")
Colin Cross758290d2019-02-01 16:42:32 -0800111
112 // mixed:
Colin Crossab898dc2019-02-21 05:03:00 +0000113 cmd = rule.Command().Tool("ld")
114 cmd.Inputs([]string{"a.o", "b.o"})
115 cmd.FlagWithOutput("-o ", "linked")
Colin Cross758290d2019-02-01 16:42:32 -0800116}
117
118func ExampleRuleBuilderCommand_Flag() {
119 fmt.Println(NewRuleBuilder().Command().
Colin Crossab898dc2019-02-21 05:03:00 +0000120 Tool("ls").Flag("-l"))
Colin Cross758290d2019-02-01 16:42:32 -0800121 // Output:
122 // ls -l
123}
124
125func ExampleRuleBuilderCommand_FlagWithArg() {
126 fmt.Println(NewRuleBuilder().Command().
Colin Crossab898dc2019-02-21 05:03:00 +0000127 Tool("ls").
Colin Cross758290d2019-02-01 16:42:32 -0800128 FlagWithArg("--sort=", "time"))
129 // Output:
130 // ls --sort=time
131}
132
Colin Crossc7ed0042019-02-11 14:11:09 -0800133func ExampleRuleBuilderCommand_FlagForEachArg() {
134 fmt.Println(NewRuleBuilder().Command().
Colin Crossab898dc2019-02-21 05:03:00 +0000135 Tool("ls").
Colin Crossc7ed0042019-02-11 14:11:09 -0800136 FlagForEachArg("--sort=", []string{"time", "size"}))
137 // Output:
138 // ls --sort=time --sort=size
139}
140
Colin Cross758290d2019-02-01 16:42:32 -0800141func ExampleRuleBuilderCommand_FlagForEachInput() {
142 fmt.Println(NewRuleBuilder().Command().
Colin Crossab898dc2019-02-21 05:03:00 +0000143 Tool("turbine").
144 FlagForEachInput("--classpath ", []string{"a.jar", "b.jar"}))
Colin Cross758290d2019-02-01 16:42:32 -0800145 // Output:
146 // turbine --classpath a.jar --classpath b.jar
147}
148
149func ExampleRuleBuilderCommand_FlagWithInputList() {
150 fmt.Println(NewRuleBuilder().Command().
Colin Crossab898dc2019-02-21 05:03:00 +0000151 Tool("java").
152 FlagWithInputList("-classpath=", []string{"a.jar", "b.jar"}, ":"))
Colin Cross758290d2019-02-01 16:42:32 -0800153 // Output:
154 // java -classpath=a.jar:b.jar
155}
156
157func ExampleRuleBuilderCommand_FlagWithInput() {
158 fmt.Println(NewRuleBuilder().Command().
Colin Crossab898dc2019-02-21 05:03:00 +0000159 Tool("java").
160 FlagWithInput("-classpath=", "a"))
Colin Cross758290d2019-02-01 16:42:32 -0800161 // Output:
162 // java -classpath=a
163}
164
165func ExampleRuleBuilderCommand_FlagWithList() {
166 fmt.Println(NewRuleBuilder().Command().
Colin Crossab898dc2019-02-21 05:03:00 +0000167 Tool("ls").
Colin Cross758290d2019-02-01 16:42:32 -0800168 FlagWithList("--sort=", []string{"time", "size"}, ","))
169 // Output:
170 // ls --sort=time,size
171}
172
Colin Crossfeec25b2019-01-30 17:32:39 -0800173func TestRuleBuilder(t *testing.T) {
Colin Cross758290d2019-02-01 16:42:32 -0800174 rule := NewRuleBuilder()
Colin Crossfeec25b2019-01-30 17:32:39 -0800175
176 cmd := rule.Command().
177 Flag("Flag").
178 FlagWithArg("FlagWithArg=", "arg").
Colin Crossab898dc2019-02-21 05:03:00 +0000179 FlagWithInput("FlagWithInput=", "input").
180 FlagWithOutput("FlagWithOutput=", "output").
181 Implicit("Implicit").
182 ImplicitOutput("ImplicitOutput").
183 Input("Input").
184 Output("Output").
Colin Crossfeec25b2019-01-30 17:32:39 -0800185 Text("Text").
Colin Crossab898dc2019-02-21 05:03:00 +0000186 Tool("Tool")
Colin Crossfeec25b2019-01-30 17:32:39 -0800187
188 rule.Command().
189 Text("command2").
Colin Crossab898dc2019-02-21 05:03:00 +0000190 Input("input2").
191 Output("output2").
192 Tool("tool2")
Colin Crossfeec25b2019-01-30 17:32:39 -0800193
194 // Test updates to the first command after the second command has been started
195 cmd.Text("after command2")
196 // Test updating a command when the previous update did not replace the cmd variable
197 cmd.Text("old cmd")
198
199 // Test a command that uses the output of a previous command as an input
200 rule.Command().
201 Text("command3").
Colin Crossab898dc2019-02-21 05:03:00 +0000202 Input("input3").
203 Input("output2").
204 Output("output3")
Colin Crossfeec25b2019-01-30 17:32:39 -0800205
206 wantCommands := []string{
Colin Crossab898dc2019-02-21 05:03:00 +0000207 "Flag FlagWithArg=arg FlagWithInput=input FlagWithOutput=output Input Output Text Tool after command2 old cmd",
208 "command2 input2 output2 tool2",
209 "command3 input3 output2 output3",
Colin Crossfeec25b2019-01-30 17:32:39 -0800210 }
Colin Crossab898dc2019-02-21 05:03:00 +0000211 wantInputs := []string{"Implicit", "Input", "input", "input2", "input3"}
212 wantOutputs := []string{"ImplicitOutput", "Output", "output", "output2", "output3"}
213 wantTools := []string{"Tool", "tool2"}
Colin Crossfeec25b2019-01-30 17:32:39 -0800214
215 if !reflect.DeepEqual(rule.Commands(), wantCommands) {
216 t.Errorf("\nwant rule.Commands() = %#v\n got %#v", wantCommands, rule.Commands())
217 }
218 if !reflect.DeepEqual(rule.Inputs(), wantInputs) {
219 t.Errorf("\nwant rule.Inputs() = %#v\n got %#v", wantInputs, rule.Inputs())
220 }
221 if !reflect.DeepEqual(rule.Outputs(), wantOutputs) {
222 t.Errorf("\nwant rule.Outputs() = %#v\n got %#v", wantOutputs, rule.Outputs())
223 }
224 if !reflect.DeepEqual(rule.Tools(), wantTools) {
225 t.Errorf("\nwant rule.Tools() = %#v\n got %#v", wantTools, rule.Tools())
226 }
227}
228
229func testRuleBuilderFactory() Module {
230 module := &testRuleBuilderModule{}
231 module.AddProperties(&module.properties)
232 InitAndroidModule(module)
233 return module
234}
235
236type testRuleBuilderModule struct {
237 ModuleBase
238 properties struct {
239 Src string
240 }
241}
242
243func (t *testRuleBuilderModule) GenerateAndroidBuildActions(ctx ModuleContext) {
Colin Crossfeec25b2019-01-30 17:32:39 -0800244 in := PathForSource(ctx, t.properties.Src)
245 out := PathForModuleOut(ctx, ctx.ModuleName())
246
Colin Cross786cd6d2019-02-01 16:41:11 -0800247 testRuleBuilder_Build(ctx, in, out)
248}
249
250type testRuleBuilderSingleton struct{}
251
252func testRuleBuilderSingletonFactory() Singleton {
253 return &testRuleBuilderSingleton{}
254}
255
256func (t *testRuleBuilderSingleton) GenerateBuildActions(ctx SingletonContext) {
257 in := PathForSource(ctx, "bar")
258 out := PathForOutput(ctx, "baz")
259 testRuleBuilder_Build(ctx, in, out)
260}
261
262func testRuleBuilder_Build(ctx BuilderContext, in Path, out WritablePath) {
Colin Cross758290d2019-02-01 16:42:32 -0800263 rule := NewRuleBuilder()
Colin Cross786cd6d2019-02-01 16:41:11 -0800264
Colin Crossab898dc2019-02-21 05:03:00 +0000265 rule.Command().Tool("cp").Input(in.String()).Output(out.String())
Colin Crossfeec25b2019-01-30 17:32:39 -0800266
267 rule.Build(pctx, ctx, "rule", "desc")
268}
269
270func TestRuleBuilder_Build(t *testing.T) {
271 buildDir, err := ioutil.TempDir("", "soong_test_rule_builder")
272 if err != nil {
273 t.Fatal(err)
274 }
275 defer os.RemoveAll(buildDir)
276
277 bp := `
278 rule_builder_test {
279 name: "foo",
280 src: "bar",
281 }
282 `
283
284 config := TestConfig(buildDir, nil)
285 ctx := NewTestContext()
286 ctx.MockFileSystem(map[string][]byte{
287 "Android.bp": []byte(bp),
288 "bar": nil,
289 "cp": nil,
290 })
291 ctx.RegisterModuleType("rule_builder_test", ModuleFactoryAdaptor(testRuleBuilderFactory))
Colin Cross786cd6d2019-02-01 16:41:11 -0800292 ctx.RegisterSingletonType("rule_builder_test", SingletonFactoryAdaptor(testRuleBuilderSingletonFactory))
Colin Crossfeec25b2019-01-30 17:32:39 -0800293 ctx.Register()
294
295 _, errs := ctx.ParseFileList(".", []string{"Android.bp"})
296 FailIfErrored(t, errs)
297 _, errs = ctx.PrepareBuildActions(config)
298 FailIfErrored(t, errs)
299
300 foo := ctx.ModuleForTests("foo", "").Rule("rule")
301
302 // TODO: make RuleParams accessible to tests and verify rule.Command().Tools() ends up in CommandDeps
303
304 if len(foo.Implicits) != 1 || foo.Implicits[0].String() != "bar" {
305 t.Errorf("want foo.Implicits = [%q], got %q", "bar", foo.Implicits.Strings())
306 }
307
308 wantOutput := filepath.Join(buildDir, ".intermediates", "foo", "foo")
309 if len(foo.Outputs) != 1 || foo.Outputs[0].String() != wantOutput {
310 t.Errorf("want foo.Outputs = [%q], got %q", wantOutput, foo.Outputs.Strings())
311 }
312
313}