blob: 53a5b489ac83e71df41f5355acb011bd80420c15 [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
Colin Crossc7ed0042019-02-11 14:11:09 -0800120func ExampleRuleBuilderCommand_FlagForEachArg() {
121 fmt.Println(NewRuleBuilder().Command().
122 Tool("ls").
123 FlagForEachArg("--sort=", []string{"time", "size"}))
124 // Output:
125 // ls --sort=time --sort=size
126}
127
Colin Cross758290d2019-02-01 16:42:32 -0800128func ExampleRuleBuilderCommand_FlagForEachInput() {
129 fmt.Println(NewRuleBuilder().Command().
130 Tool("turbine").
131 FlagForEachInput("--classpath ", []string{"a.jar", "b.jar"}))
132 // Output:
133 // turbine --classpath a.jar --classpath b.jar
134}
135
136func ExampleRuleBuilderCommand_FlagWithInputList() {
137 fmt.Println(NewRuleBuilder().Command().
138 Tool("java").
139 FlagWithInputList("-classpath=", []string{"a.jar", "b.jar"}, ":"))
140 // Output:
141 // java -classpath=a.jar:b.jar
142}
143
144func ExampleRuleBuilderCommand_FlagWithInput() {
145 fmt.Println(NewRuleBuilder().Command().
146 Tool("java").
147 FlagWithInput("-classpath=", "a"))
148 // Output:
149 // java -classpath=a
150}
151
152func ExampleRuleBuilderCommand_FlagWithList() {
153 fmt.Println(NewRuleBuilder().Command().
154 Tool("ls").
155 FlagWithList("--sort=", []string{"time", "size"}, ","))
156 // Output:
157 // ls --sort=time,size
158}
159
Colin Crossfeec25b2019-01-30 17:32:39 -0800160func TestRuleBuilder(t *testing.T) {
Colin Cross758290d2019-02-01 16:42:32 -0800161 rule := NewRuleBuilder()
Colin Crossfeec25b2019-01-30 17:32:39 -0800162
163 cmd := rule.Command().
164 Flag("Flag").
165 FlagWithArg("FlagWithArg=", "arg").
166 FlagWithInput("FlagWithInput=", "input").
167 FlagWithOutput("FlagWithOutput=", "output").
168 Implicit("Implicit").
169 ImplicitOutput("ImplicitOutput").
170 Input("Input").
171 Output("Output").
172 Text("Text").
173 Tool("Tool")
174
175 rule.Command().
176 Text("command2").
177 Input("input2").
178 Output("output2").
179 Tool("tool2")
180
181 // Test updates to the first command after the second command has been started
182 cmd.Text("after command2")
183 // Test updating a command when the previous update did not replace the cmd variable
184 cmd.Text("old cmd")
185
186 // Test a command that uses the output of a previous command as an input
187 rule.Command().
188 Text("command3").
189 Input("input3").
190 Input("output2").
191 Output("output3")
192
193 wantCommands := []string{
194 "Flag FlagWithArg=arg FlagWithInput=input FlagWithOutput=output Input Output Text Tool after command2 old cmd",
195 "command2 input2 output2 tool2",
196 "command3 input3 output2 output3",
197 }
198 wantInputs := []string{"Implicit", "Input", "input", "input2", "input3"}
199 wantOutputs := []string{"ImplicitOutput", "Output", "output", "output2", "output3"}
200 wantTools := []string{"Tool", "tool2"}
201
202 if !reflect.DeepEqual(rule.Commands(), wantCommands) {
203 t.Errorf("\nwant rule.Commands() = %#v\n got %#v", wantCommands, rule.Commands())
204 }
205 if !reflect.DeepEqual(rule.Inputs(), wantInputs) {
206 t.Errorf("\nwant rule.Inputs() = %#v\n got %#v", wantInputs, rule.Inputs())
207 }
208 if !reflect.DeepEqual(rule.Outputs(), wantOutputs) {
209 t.Errorf("\nwant rule.Outputs() = %#v\n got %#v", wantOutputs, rule.Outputs())
210 }
211 if !reflect.DeepEqual(rule.Tools(), wantTools) {
212 t.Errorf("\nwant rule.Tools() = %#v\n got %#v", wantTools, rule.Tools())
213 }
214}
215
216func testRuleBuilderFactory() Module {
217 module := &testRuleBuilderModule{}
218 module.AddProperties(&module.properties)
219 InitAndroidModule(module)
220 return module
221}
222
223type testRuleBuilderModule struct {
224 ModuleBase
225 properties struct {
226 Src string
227 }
228}
229
230func (t *testRuleBuilderModule) GenerateAndroidBuildActions(ctx ModuleContext) {
Colin Crossfeec25b2019-01-30 17:32:39 -0800231 in := PathForSource(ctx, t.properties.Src)
232 out := PathForModuleOut(ctx, ctx.ModuleName())
233
Colin Cross786cd6d2019-02-01 16:41:11 -0800234 testRuleBuilder_Build(ctx, in, out)
235}
236
237type testRuleBuilderSingleton struct{}
238
239func testRuleBuilderSingletonFactory() Singleton {
240 return &testRuleBuilderSingleton{}
241}
242
243func (t *testRuleBuilderSingleton) GenerateBuildActions(ctx SingletonContext) {
244 in := PathForSource(ctx, "bar")
245 out := PathForOutput(ctx, "baz")
246 testRuleBuilder_Build(ctx, in, out)
247}
248
249func testRuleBuilder_Build(ctx BuilderContext, in Path, out WritablePath) {
Colin Cross758290d2019-02-01 16:42:32 -0800250 rule := NewRuleBuilder()
Colin Cross786cd6d2019-02-01 16:41:11 -0800251
Colin Crossfeec25b2019-01-30 17:32:39 -0800252 rule.Command().Tool("cp").Input(in.String()).Output(out.String())
253
254 rule.Build(pctx, ctx, "rule", "desc")
255}
256
257func TestRuleBuilder_Build(t *testing.T) {
258 buildDir, err := ioutil.TempDir("", "soong_test_rule_builder")
259 if err != nil {
260 t.Fatal(err)
261 }
262 defer os.RemoveAll(buildDir)
263
264 bp := `
265 rule_builder_test {
266 name: "foo",
267 src: "bar",
268 }
269 `
270
271 config := TestConfig(buildDir, nil)
272 ctx := NewTestContext()
273 ctx.MockFileSystem(map[string][]byte{
274 "Android.bp": []byte(bp),
275 "bar": nil,
276 "cp": nil,
277 })
278 ctx.RegisterModuleType("rule_builder_test", ModuleFactoryAdaptor(testRuleBuilderFactory))
Colin Cross786cd6d2019-02-01 16:41:11 -0800279 ctx.RegisterSingletonType("rule_builder_test", SingletonFactoryAdaptor(testRuleBuilderSingletonFactory))
Colin Crossfeec25b2019-01-30 17:32:39 -0800280 ctx.Register()
281
282 _, errs := ctx.ParseFileList(".", []string{"Android.bp"})
283 FailIfErrored(t, errs)
284 _, errs = ctx.PrepareBuildActions(config)
285 FailIfErrored(t, errs)
286
287 foo := ctx.ModuleForTests("foo", "").Rule("rule")
288
289 // TODO: make RuleParams accessible to tests and verify rule.Command().Tools() ends up in CommandDeps
290
291 if len(foo.Implicits) != 1 || foo.Implicits[0].String() != "bar" {
292 t.Errorf("want foo.Implicits = [%q], got %q", "bar", foo.Implicits.Strings())
293 }
294
295 wantOutput := filepath.Join(buildDir, ".intermediates", "foo", "foo")
296 if len(foo.Outputs) != 1 || foo.Outputs[0].String() != wantOutput {
297 t.Errorf("want foo.Outputs = [%q], got %q", wantOutput, foo.Outputs.Strings())
298 }
299
300}