blob: c896719e9c36a257543310319f60b4c2749fae0a [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
48func ExampleRuleBuilderCommand() {
49 rule := NewRuleBuilder()
50
51 // chained
52 rule.Command().Tool("ld").Inputs([]string{"a.o", "b.o"}).FlagWithOutput("-o ", "linked")
53
54 // unchained
55 cmd := rule.Command()
56 cmd.Tool("ld")
57 cmd.Inputs([]string{"a.o", "b.o"})
58 cmd.FlagWithOutput("-o ", "linked")
59
60 // mixed:
61 cmd = rule.Command().Tool("ld")
62 cmd.Inputs([]string{"a.o", "b.o"})
63 cmd.FlagWithOutput("-o ", "linked")
64}
65
66func ExampleRuleBuilderCommand_Flag() {
67 fmt.Println(NewRuleBuilder().Command().
68 Tool("ls").Flag("-l"))
69 // Output:
70 // ls -l
71}
72
73func ExampleRuleBuilderCommand_FlagWithArg() {
74 fmt.Println(NewRuleBuilder().Command().
75 Tool("ls").
76 FlagWithArg("--sort=", "time"))
77 // Output:
78 // ls --sort=time
79}
80
81func ExampleRuleBuilderCommand_FlagForEachInput() {
82 fmt.Println(NewRuleBuilder().Command().
83 Tool("turbine").
84 FlagForEachInput("--classpath ", []string{"a.jar", "b.jar"}))
85 // Output:
86 // turbine --classpath a.jar --classpath b.jar
87}
88
89func ExampleRuleBuilderCommand_FlagWithInputList() {
90 fmt.Println(NewRuleBuilder().Command().
91 Tool("java").
92 FlagWithInputList("-classpath=", []string{"a.jar", "b.jar"}, ":"))
93 // Output:
94 // java -classpath=a.jar:b.jar
95}
96
97func ExampleRuleBuilderCommand_FlagWithInput() {
98 fmt.Println(NewRuleBuilder().Command().
99 Tool("java").
100 FlagWithInput("-classpath=", "a"))
101 // Output:
102 // java -classpath=a
103}
104
105func ExampleRuleBuilderCommand_FlagWithList() {
106 fmt.Println(NewRuleBuilder().Command().
107 Tool("ls").
108 FlagWithList("--sort=", []string{"time", "size"}, ","))
109 // Output:
110 // ls --sort=time,size
111}
112
Colin Crossfeec25b2019-01-30 17:32:39 -0800113func TestRuleBuilder(t *testing.T) {
Colin Cross758290d2019-02-01 16:42:32 -0800114 rule := NewRuleBuilder()
Colin Crossfeec25b2019-01-30 17:32:39 -0800115
116 cmd := rule.Command().
117 Flag("Flag").
118 FlagWithArg("FlagWithArg=", "arg").
119 FlagWithInput("FlagWithInput=", "input").
120 FlagWithOutput("FlagWithOutput=", "output").
121 Implicit("Implicit").
122 ImplicitOutput("ImplicitOutput").
123 Input("Input").
124 Output("Output").
125 Text("Text").
126 Tool("Tool")
127
128 rule.Command().
129 Text("command2").
130 Input("input2").
131 Output("output2").
132 Tool("tool2")
133
134 // Test updates to the first command after the second command has been started
135 cmd.Text("after command2")
136 // Test updating a command when the previous update did not replace the cmd variable
137 cmd.Text("old cmd")
138
139 // Test a command that uses the output of a previous command as an input
140 rule.Command().
141 Text("command3").
142 Input("input3").
143 Input("output2").
144 Output("output3")
145
146 wantCommands := []string{
147 "Flag FlagWithArg=arg FlagWithInput=input FlagWithOutput=output Input Output Text Tool after command2 old cmd",
148 "command2 input2 output2 tool2",
149 "command3 input3 output2 output3",
150 }
151 wantInputs := []string{"Implicit", "Input", "input", "input2", "input3"}
152 wantOutputs := []string{"ImplicitOutput", "Output", "output", "output2", "output3"}
153 wantTools := []string{"Tool", "tool2"}
154
155 if !reflect.DeepEqual(rule.Commands(), wantCommands) {
156 t.Errorf("\nwant rule.Commands() = %#v\n got %#v", wantCommands, rule.Commands())
157 }
158 if !reflect.DeepEqual(rule.Inputs(), wantInputs) {
159 t.Errorf("\nwant rule.Inputs() = %#v\n got %#v", wantInputs, rule.Inputs())
160 }
161 if !reflect.DeepEqual(rule.Outputs(), wantOutputs) {
162 t.Errorf("\nwant rule.Outputs() = %#v\n got %#v", wantOutputs, rule.Outputs())
163 }
164 if !reflect.DeepEqual(rule.Tools(), wantTools) {
165 t.Errorf("\nwant rule.Tools() = %#v\n got %#v", wantTools, rule.Tools())
166 }
167}
168
169func testRuleBuilderFactory() Module {
170 module := &testRuleBuilderModule{}
171 module.AddProperties(&module.properties)
172 InitAndroidModule(module)
173 return module
174}
175
176type testRuleBuilderModule struct {
177 ModuleBase
178 properties struct {
179 Src string
180 }
181}
182
183func (t *testRuleBuilderModule) GenerateAndroidBuildActions(ctx ModuleContext) {
Colin Crossfeec25b2019-01-30 17:32:39 -0800184 in := PathForSource(ctx, t.properties.Src)
185 out := PathForModuleOut(ctx, ctx.ModuleName())
186
Colin Cross786cd6d2019-02-01 16:41:11 -0800187 testRuleBuilder_Build(ctx, in, out)
188}
189
190type testRuleBuilderSingleton struct{}
191
192func testRuleBuilderSingletonFactory() Singleton {
193 return &testRuleBuilderSingleton{}
194}
195
196func (t *testRuleBuilderSingleton) GenerateBuildActions(ctx SingletonContext) {
197 in := PathForSource(ctx, "bar")
198 out := PathForOutput(ctx, "baz")
199 testRuleBuilder_Build(ctx, in, out)
200}
201
202func testRuleBuilder_Build(ctx BuilderContext, in Path, out WritablePath) {
Colin Cross758290d2019-02-01 16:42:32 -0800203 rule := NewRuleBuilder()
Colin Cross786cd6d2019-02-01 16:41:11 -0800204
Colin Crossfeec25b2019-01-30 17:32:39 -0800205 rule.Command().Tool("cp").Input(in.String()).Output(out.String())
206
207 rule.Build(pctx, ctx, "rule", "desc")
208}
209
210func TestRuleBuilder_Build(t *testing.T) {
211 buildDir, err := ioutil.TempDir("", "soong_test_rule_builder")
212 if err != nil {
213 t.Fatal(err)
214 }
215 defer os.RemoveAll(buildDir)
216
217 bp := `
218 rule_builder_test {
219 name: "foo",
220 src: "bar",
221 }
222 `
223
224 config := TestConfig(buildDir, nil)
225 ctx := NewTestContext()
226 ctx.MockFileSystem(map[string][]byte{
227 "Android.bp": []byte(bp),
228 "bar": nil,
229 "cp": nil,
230 })
231 ctx.RegisterModuleType("rule_builder_test", ModuleFactoryAdaptor(testRuleBuilderFactory))
Colin Cross786cd6d2019-02-01 16:41:11 -0800232 ctx.RegisterSingletonType("rule_builder_test", SingletonFactoryAdaptor(testRuleBuilderSingletonFactory))
Colin Crossfeec25b2019-01-30 17:32:39 -0800233 ctx.Register()
234
235 _, errs := ctx.ParseFileList(".", []string{"Android.bp"})
236 FailIfErrored(t, errs)
237 _, errs = ctx.PrepareBuildActions(config)
238 FailIfErrored(t, errs)
239
240 foo := ctx.ModuleForTests("foo", "").Rule("rule")
241
242 // TODO: make RuleParams accessible to tests and verify rule.Command().Tools() ends up in CommandDeps
243
244 if len(foo.Implicits) != 1 || foo.Implicits[0].String() != "bar" {
245 t.Errorf("want foo.Implicits = [%q], got %q", "bar", foo.Implicits.Strings())
246 }
247
248 wantOutput := filepath.Join(buildDir, ".intermediates", "foo", "foo")
249 if len(foo.Outputs) != 1 || foo.Outputs[0].String() != wantOutput {
250 t.Errorf("want foo.Outputs = [%q], got %q", wantOutput, foo.Outputs.Strings())
251 }
252
253}