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
30 rule.Command().Tool("ld").Inputs([]string{"a.o", "b.o"}).FlagWithOutput("-o ", "linked")
31 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:
42 // commands: "ld a.o b.o -o linked && echo success"
43 // tools: ["ld"]
44 // inputs: ["a.o" "b.o"]
45 // outputs: ["linked"]
46}
47
Colin Cross5cb5b092019-02-02 21:25:18 -080048func ExampleRuleBuilder_Temporary() {
49 rule := NewRuleBuilder()
50
51 rule.Command().Tool("cp").Input("a").Output("b")
52 rule.Command().Tool("cp").Input("b").Output("c")
53 rule.Temporary("b")
54
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:
61 // commands: "cp a b && cp b c"
62 // tools: ["cp"]
63 // inputs: ["a"]
64 // outputs: ["c"]
65}
66
67func ExampleRuleBuilder_DeleteTemporaryFiles() {
68 rule := NewRuleBuilder()
69
70 rule.Command().Tool("cp").Input("a").Output("b")
71 rule.Command().Tool("cp").Input("b").Output("c")
72 rule.Temporary("b")
73 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:
81 // commands: "cp a b && cp b c && rm -f b"
82 // tools: ["cp"]
83 // inputs: ["a"]
84 // outputs: ["c"]
85}
86
Colin Crossdeabb942019-02-11 14:11:09 -080087func ExampleRuleBuilder_Installs() {
88 rule := NewRuleBuilder()
89
90 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")
93
94 fmt.Printf("rule.Installs().String() = %q\n", rule.Installs().String())
95
96 // Output:
97 // rule.Installs().String() = "linked:/bin/linked linked:/sbin/linked"
98}
99
Colin Cross758290d2019-02-01 16:42:32 -0800100func ExampleRuleBuilderCommand() {
101 rule := NewRuleBuilder()
102
103 // chained
104 rule.Command().Tool("ld").Inputs([]string{"a.o", "b.o"}).FlagWithOutput("-o ", "linked")
105
106 // unchained
107 cmd := rule.Command()
108 cmd.Tool("ld")
109 cmd.Inputs([]string{"a.o", "b.o"})
110 cmd.FlagWithOutput("-o ", "linked")
111
112 // mixed:
113 cmd = rule.Command().Tool("ld")
114 cmd.Inputs([]string{"a.o", "b.o"})
115 cmd.FlagWithOutput("-o ", "linked")
116}
117
118func ExampleRuleBuilderCommand_Flag() {
119 fmt.Println(NewRuleBuilder().Command().
120 Tool("ls").Flag("-l"))
121 // Output:
122 // ls -l
123}
124
125func ExampleRuleBuilderCommand_FlagWithArg() {
126 fmt.Println(NewRuleBuilder().Command().
127 Tool("ls").
128 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().
135 Tool("ls").
136 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().
143 Tool("turbine").
144 FlagForEachInput("--classpath ", []string{"a.jar", "b.jar"}))
145 // Output:
146 // turbine --classpath a.jar --classpath b.jar
147}
148
149func ExampleRuleBuilderCommand_FlagWithInputList() {
150 fmt.Println(NewRuleBuilder().Command().
151 Tool("java").
152 FlagWithInputList("-classpath=", []string{"a.jar", "b.jar"}, ":"))
153 // Output:
154 // java -classpath=a.jar:b.jar
155}
156
157func ExampleRuleBuilderCommand_FlagWithInput() {
158 fmt.Println(NewRuleBuilder().Command().
159 Tool("java").
160 FlagWithInput("-classpath=", "a"))
161 // Output:
162 // java -classpath=a
163}
164
165func ExampleRuleBuilderCommand_FlagWithList() {
166 fmt.Println(NewRuleBuilder().Command().
167 Tool("ls").
168 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").
179 FlagWithInput("FlagWithInput=", "input").
180 FlagWithOutput("FlagWithOutput=", "output").
181 Implicit("Implicit").
182 ImplicitOutput("ImplicitOutput").
183 Input("Input").
184 Output("Output").
185 Text("Text").
186 Tool("Tool")
187
188 rule.Command().
189 Text("command2").
190 Input("input2").
191 Output("output2").
192 Tool("tool2")
193
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").
202 Input("input3").
203 Input("output2").
204 Output("output3")
205
206 wantCommands := []string{
207 "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",
210 }
211 wantInputs := []string{"Implicit", "Input", "input", "input2", "input3"}
212 wantOutputs := []string{"ImplicitOutput", "Output", "output", "output2", "output3"}
213 wantTools := []string{"Tool", "tool2"}
214
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 Crossfeec25b2019-01-30 17:32:39 -0800265 rule.Command().Tool("cp").Input(in.String()).Output(out.String())
266
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}