blob: f7577a6e1948ed08577eab1b7bfc5651959e6157 [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 Cross758290d2019-02-01 16:42:32 -080087func ExampleRuleBuilderCommand() {
88 rule := NewRuleBuilder()
89
90 // chained
91 rule.Command().Tool("ld").Inputs([]string{"a.o", "b.o"}).FlagWithOutput("-o ", "linked")
92
93 // unchained
94 cmd := rule.Command()
95 cmd.Tool("ld")
96 cmd.Inputs([]string{"a.o", "b.o"})
97 cmd.FlagWithOutput("-o ", "linked")
98
99 // mixed:
100 cmd = rule.Command().Tool("ld")
101 cmd.Inputs([]string{"a.o", "b.o"})
102 cmd.FlagWithOutput("-o ", "linked")
103}
104
105func ExampleRuleBuilderCommand_Flag() {
106 fmt.Println(NewRuleBuilder().Command().
107 Tool("ls").Flag("-l"))
108 // Output:
109 // ls -l
110}
111
112func ExampleRuleBuilderCommand_FlagWithArg() {
113 fmt.Println(NewRuleBuilder().Command().
114 Tool("ls").
115 FlagWithArg("--sort=", "time"))
116 // Output:
117 // ls --sort=time
118}
119
120func ExampleRuleBuilderCommand_FlagForEachInput() {
121 fmt.Println(NewRuleBuilder().Command().
122 Tool("turbine").
123 FlagForEachInput("--classpath ", []string{"a.jar", "b.jar"}))
124 // Output:
125 // turbine --classpath a.jar --classpath b.jar
126}
127
128func ExampleRuleBuilderCommand_FlagWithInputList() {
129 fmt.Println(NewRuleBuilder().Command().
130 Tool("java").
131 FlagWithInputList("-classpath=", []string{"a.jar", "b.jar"}, ":"))
132 // Output:
133 // java -classpath=a.jar:b.jar
134}
135
136func ExampleRuleBuilderCommand_FlagWithInput() {
137 fmt.Println(NewRuleBuilder().Command().
138 Tool("java").
139 FlagWithInput("-classpath=", "a"))
140 // Output:
141 // java -classpath=a
142}
143
144func ExampleRuleBuilderCommand_FlagWithList() {
145 fmt.Println(NewRuleBuilder().Command().
146 Tool("ls").
147 FlagWithList("--sort=", []string{"time", "size"}, ","))
148 // Output:
149 // ls --sort=time,size
150}
151
Colin Crossfeec25b2019-01-30 17:32:39 -0800152func TestRuleBuilder(t *testing.T) {
Colin Cross758290d2019-02-01 16:42:32 -0800153 rule := NewRuleBuilder()
Colin Crossfeec25b2019-01-30 17:32:39 -0800154
155 cmd := rule.Command().
156 Flag("Flag").
157 FlagWithArg("FlagWithArg=", "arg").
158 FlagWithInput("FlagWithInput=", "input").
159 FlagWithOutput("FlagWithOutput=", "output").
160 Implicit("Implicit").
161 ImplicitOutput("ImplicitOutput").
162 Input("Input").
163 Output("Output").
164 Text("Text").
165 Tool("Tool")
166
167 rule.Command().
168 Text("command2").
169 Input("input2").
170 Output("output2").
171 Tool("tool2")
172
173 // Test updates to the first command after the second command has been started
174 cmd.Text("after command2")
175 // Test updating a command when the previous update did not replace the cmd variable
176 cmd.Text("old cmd")
177
178 // Test a command that uses the output of a previous command as an input
179 rule.Command().
180 Text("command3").
181 Input("input3").
182 Input("output2").
183 Output("output3")
184
185 wantCommands := []string{
186 "Flag FlagWithArg=arg FlagWithInput=input FlagWithOutput=output Input Output Text Tool after command2 old cmd",
187 "command2 input2 output2 tool2",
188 "command3 input3 output2 output3",
189 }
190 wantInputs := []string{"Implicit", "Input", "input", "input2", "input3"}
191 wantOutputs := []string{"ImplicitOutput", "Output", "output", "output2", "output3"}
192 wantTools := []string{"Tool", "tool2"}
193
194 if !reflect.DeepEqual(rule.Commands(), wantCommands) {
195 t.Errorf("\nwant rule.Commands() = %#v\n got %#v", wantCommands, rule.Commands())
196 }
197 if !reflect.DeepEqual(rule.Inputs(), wantInputs) {
198 t.Errorf("\nwant rule.Inputs() = %#v\n got %#v", wantInputs, rule.Inputs())
199 }
200 if !reflect.DeepEqual(rule.Outputs(), wantOutputs) {
201 t.Errorf("\nwant rule.Outputs() = %#v\n got %#v", wantOutputs, rule.Outputs())
202 }
203 if !reflect.DeepEqual(rule.Tools(), wantTools) {
204 t.Errorf("\nwant rule.Tools() = %#v\n got %#v", wantTools, rule.Tools())
205 }
206}
207
208func testRuleBuilderFactory() Module {
209 module := &testRuleBuilderModule{}
210 module.AddProperties(&module.properties)
211 InitAndroidModule(module)
212 return module
213}
214
215type testRuleBuilderModule struct {
216 ModuleBase
217 properties struct {
218 Src string
219 }
220}
221
222func (t *testRuleBuilderModule) GenerateAndroidBuildActions(ctx ModuleContext) {
Colin Crossfeec25b2019-01-30 17:32:39 -0800223 in := PathForSource(ctx, t.properties.Src)
224 out := PathForModuleOut(ctx, ctx.ModuleName())
225
Colin Cross786cd6d2019-02-01 16:41:11 -0800226 testRuleBuilder_Build(ctx, in, out)
227}
228
229type testRuleBuilderSingleton struct{}
230
231func testRuleBuilderSingletonFactory() Singleton {
232 return &testRuleBuilderSingleton{}
233}
234
235func (t *testRuleBuilderSingleton) GenerateBuildActions(ctx SingletonContext) {
236 in := PathForSource(ctx, "bar")
237 out := PathForOutput(ctx, "baz")
238 testRuleBuilder_Build(ctx, in, out)
239}
240
241func testRuleBuilder_Build(ctx BuilderContext, in Path, out WritablePath) {
Colin Cross758290d2019-02-01 16:42:32 -0800242 rule := NewRuleBuilder()
Colin Cross786cd6d2019-02-01 16:41:11 -0800243
Colin Crossfeec25b2019-01-30 17:32:39 -0800244 rule.Command().Tool("cp").Input(in.String()).Output(out.String())
245
246 rule.Build(pctx, ctx, "rule", "desc")
247}
248
249func TestRuleBuilder_Build(t *testing.T) {
250 buildDir, err := ioutil.TempDir("", "soong_test_rule_builder")
251 if err != nil {
252 t.Fatal(err)
253 }
254 defer os.RemoveAll(buildDir)
255
256 bp := `
257 rule_builder_test {
258 name: "foo",
259 src: "bar",
260 }
261 `
262
263 config := TestConfig(buildDir, nil)
264 ctx := NewTestContext()
265 ctx.MockFileSystem(map[string][]byte{
266 "Android.bp": []byte(bp),
267 "bar": nil,
268 "cp": nil,
269 })
270 ctx.RegisterModuleType("rule_builder_test", ModuleFactoryAdaptor(testRuleBuilderFactory))
Colin Cross786cd6d2019-02-01 16:41:11 -0800271 ctx.RegisterSingletonType("rule_builder_test", SingletonFactoryAdaptor(testRuleBuilderSingletonFactory))
Colin Crossfeec25b2019-01-30 17:32:39 -0800272 ctx.Register()
273
274 _, errs := ctx.ParseFileList(".", []string{"Android.bp"})
275 FailIfErrored(t, errs)
276 _, errs = ctx.PrepareBuildActions(config)
277 FailIfErrored(t, errs)
278
279 foo := ctx.ModuleForTests("foo", "").Rule("rule")
280
281 // TODO: make RuleParams accessible to tests and verify rule.Command().Tools() ends up in CommandDeps
282
283 if len(foo.Implicits) != 1 || foo.Implicits[0].String() != "bar" {
284 t.Errorf("want foo.Implicits = [%q], got %q", "bar", foo.Implicits.Strings())
285 }
286
287 wantOutput := filepath.Join(buildDir, ".intermediates", "foo", "foo")
288 if len(foo.Outputs) != 1 || foo.Outputs[0].String() != wantOutput {
289 t.Errorf("want foo.Outputs = [%q], got %q", wantOutput, foo.Outputs.Strings())
290 }
291
292}