1// 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 ( 18 "fmt" 19 "io/ioutil" 20 "os" 21 "path/filepath" 22 "reflect" 23 "strings" 24 "testing" 25) 26 27func pathContext() PathContext { 28 return PathContextForTesting(TestConfig("out", nil), 29 map[string][]byte{ 30 "ld": nil, 31 "a.o": nil, 32 "b.o": nil, 33 "cp": nil, 34 "a": nil, 35 "b": nil, 36 "ls": nil, 37 "turbine": nil, 38 "java": nil, 39 }) 40} 41 42func ExampleRuleBuilder() { 43 rule := NewRuleBuilder() 44 45 ctx := pathContext() 46 47 rule.Command(). 48 Tool(PathForSource(ctx, "ld")). 49 Inputs(PathsForTesting("a.o", "b.o")). 50 FlagWithOutput("-o ", PathForOutput(ctx, "linked")) 51 rule.Command().Text("echo success") 52 53 // To add the command to the build graph: 54 // rule.Build(pctx, ctx, "link", "link") 55 56 fmt.Printf("commands: %q\n", strings.Join(rule.Commands(), " && ")) 57 fmt.Printf("tools: %q\n", rule.Tools()) 58 fmt.Printf("inputs: %q\n", rule.Inputs()) 59 fmt.Printf("outputs: %q\n", rule.Outputs()) 60 61 // Output: 62 // commands: "ld a.o b.o -o out/linked && echo success" 63 // tools: ["ld"] 64 // inputs: ["a.o" "b.o"] 65 // outputs: ["out/linked"] 66} 67 68func ExampleRuleBuilder_Temporary() { 69 rule := NewRuleBuilder() 70 71 ctx := pathContext() 72 73 rule.Command(). 74 Tool(PathForSource(ctx, "cp")). 75 Input(PathForSource(ctx, "a")). 76 Output(PathForOutput(ctx, "b")) 77 rule.Command(). 78 Tool(PathForSource(ctx, "cp")). 79 Input(PathForOutput(ctx, "b")). 80 Output(PathForOutput(ctx, "c")) 81 rule.Temporary(PathForOutput(ctx, "b")) 82 83 fmt.Printf("commands: %q\n", strings.Join(rule.Commands(), " && ")) 84 fmt.Printf("tools: %q\n", rule.Tools()) 85 fmt.Printf("inputs: %q\n", rule.Inputs()) 86 fmt.Printf("outputs: %q\n", rule.Outputs()) 87 88 // Output: 89 // commands: "cp a out/b && cp out/b out/c" 90 // tools: ["cp"] 91 // inputs: ["a"] 92 // outputs: ["out/c"] 93} 94 95func ExampleRuleBuilder_DeleteTemporaryFiles() { 96 rule := NewRuleBuilder() 97 98 ctx := pathContext() 99 100 rule.Command(). 101 Tool(PathForSource(ctx, "cp")). 102 Input(PathForSource(ctx, "a")). 103 Output(PathForOutput(ctx, "b")) 104 rule.Command(). 105 Tool(PathForSource(ctx, "cp")). 106 Input(PathForOutput(ctx, "b")). 107 Output(PathForOutput(ctx, "c")) 108 rule.Temporary(PathForOutput(ctx, "b")) 109 rule.DeleteTemporaryFiles() 110 111 fmt.Printf("commands: %q\n", strings.Join(rule.Commands(), " && ")) 112 fmt.Printf("tools: %q\n", rule.Tools()) 113 fmt.Printf("inputs: %q\n", rule.Inputs()) 114 fmt.Printf("outputs: %q\n", rule.Outputs()) 115 116 // Output: 117 // commands: "cp a out/b && cp out/b out/c && rm -f out/b" 118 // tools: ["cp"] 119 // inputs: ["a"] 120 // outputs: ["out/c"] 121} 122 123func ExampleRuleBuilder_Installs() { 124 rule := NewRuleBuilder() 125 126 ctx := pathContext() 127 128 out := PathForOutput(ctx, "linked") 129 130 rule.Command(). 131 Tool(PathForSource(ctx, "ld")). 132 Inputs(PathsForTesting("a.o", "b.o")). 133 FlagWithOutput("-o ", out) 134 rule.Install(out, "/bin/linked") 135 rule.Install(out, "/sbin/linked") 136 137 fmt.Printf("rule.Installs().String() = %q\n", rule.Installs().String()) 138 139 // Output: 140 // rule.Installs().String() = "out/linked:/bin/linked out/linked:/sbin/linked" 141} 142 143func ExampleRuleBuilderCommand() { 144 rule := NewRuleBuilder() 145 146 ctx := pathContext() 147 148 // chained 149 rule.Command(). 150 Tool(PathForSource(ctx, "ld")). 151 Inputs(PathsForTesting("a.o", "b.o")). 152 FlagWithOutput("-o ", PathForOutput(ctx, "linked")) 153 154 // unchained 155 cmd := rule.Command() 156 cmd.Tool(PathForSource(ctx, "ld")) 157 cmd.Inputs(PathsForTesting("a.o", "b.o")) 158 cmd.FlagWithOutput("-o ", PathForOutput(ctx, "linked")) 159 160 // mixed: 161 cmd = rule.Command().Tool(PathForSource(ctx, "ld")) 162 cmd.Inputs(PathsForTesting("a.o", "b.o")) 163 cmd.FlagWithOutput("-o ", PathForOutput(ctx, "linked")) 164} 165 166func ExampleRuleBuilderCommand_Flag() { 167 ctx := pathContext() 168 fmt.Println(NewRuleBuilder().Command(). 169 Tool(PathForSource(ctx, "ls")).Flag("-l")) 170 // Output: 171 // ls -l 172} 173 174func ExampleRuleBuilderCommand_Flags() { 175 ctx := pathContext() 176 fmt.Println(NewRuleBuilder().Command(). 177 Tool(PathForSource(ctx, "ls")).Flags([]string{"-l", "-a"})) 178 // Output: 179 // ls -l -a 180} 181 182func ExampleRuleBuilderCommand_FlagWithArg() { 183 ctx := pathContext() 184 fmt.Println(NewRuleBuilder().Command(). 185 Tool(PathForSource(ctx, "ls")). 186 FlagWithArg("--sort=", "time")) 187 // Output: 188 // ls --sort=time 189} 190 191func ExampleRuleBuilderCommand_FlagForEachArg() { 192 ctx := pathContext() 193 fmt.Println(NewRuleBuilder().Command(). 194 Tool(PathForSource(ctx, "ls")). 195 FlagForEachArg("--sort=", []string{"time", "size"})) 196 // Output: 197 // ls --sort=time --sort=size 198} 199 200func ExampleRuleBuilderCommand_FlagForEachInput() { 201 ctx := pathContext() 202 fmt.Println(NewRuleBuilder().Command(). 203 Tool(PathForSource(ctx, "turbine")). 204 FlagForEachInput("--classpath ", PathsForTesting("a.jar", "b.jar"))) 205 // Output: 206 // turbine --classpath a.jar --classpath b.jar 207} 208 209func ExampleRuleBuilderCommand_FlagWithInputList() { 210 ctx := pathContext() 211 fmt.Println(NewRuleBuilder().Command(). 212 Tool(PathForSource(ctx, "java")). 213 FlagWithInputList("-classpath=", PathsForTesting("a.jar", "b.jar"), ":")) 214 // Output: 215 // java -classpath=a.jar:b.jar 216} 217 218func ExampleRuleBuilderCommand_FlagWithInput() { 219 ctx := pathContext() 220 fmt.Println(NewRuleBuilder().Command(). 221 Tool(PathForSource(ctx, "java")). 222 FlagWithInput("-classpath=", PathForSource(ctx, "a"))) 223 // Output: 224 // java -classpath=a 225} 226 227func ExampleRuleBuilderCommand_FlagWithList() { 228 ctx := pathContext() 229 fmt.Println(NewRuleBuilder().Command(). 230 Tool(PathForSource(ctx, "ls")). 231 FlagWithList("--sort=", []string{"time", "size"}, ",")) 232 // Output: 233 // ls --sort=time,size 234} 235 236func TestRuleBuilder(t *testing.T) { 237 rule := NewRuleBuilder() 238 239 fs := map[string][]byte{ 240 "dep_fixer": nil, 241 "input": nil, 242 "Implicit": nil, 243 "Input": nil, 244 "Tool": nil, 245 "input2": nil, 246 "tool2": nil, 247 "input3": nil, 248 } 249 250 ctx := PathContextForTesting(TestConfig("out", nil), fs) 251 252 cmd := rule.Command(). 253 DepFile(PathForOutput(ctx, "DepFile")). 254 Flag("Flag"). 255 FlagWithArg("FlagWithArg=", "arg"). 256 FlagWithDepFile("FlagWithDepFile=", PathForOutput(ctx, "depfile")). 257 FlagWithInput("FlagWithInput=", PathForSource(ctx, "input")). 258 FlagWithOutput("FlagWithOutput=", PathForOutput(ctx, "output")). 259 Implicit(PathForSource(ctx, "Implicit")). 260 ImplicitDepFile(PathForOutput(ctx, "ImplicitDepFile")). 261 ImplicitOutput(PathForOutput(ctx, "ImplicitOutput")). 262 Input(PathForSource(ctx, "Input")). 263 Output(PathForOutput(ctx, "Output")). 264 Text("Text"). 265 Tool(PathForSource(ctx, "Tool")) 266 267 rule.Command(). 268 Text("command2"). 269 DepFile(PathForOutput(ctx, "depfile2")). 270 Input(PathForSource(ctx, "input2")). 271 Output(PathForOutput(ctx, "output2")). 272 Tool(PathForSource(ctx, "tool2")) 273 274 // Test updates to the first command after the second command has been started 275 cmd.Text("after command2") 276 // Test updating a command when the previous update did not replace the cmd variable 277 cmd.Text("old cmd") 278 279 // Test a command that uses the output of a previous command as an input 280 rule.Command(). 281 Text("command3"). 282 Input(PathForSource(ctx, "input3")). 283 Input(PathForOutput(ctx, "output2")). 284 Output(PathForOutput(ctx, "output3")) 285 286 wantCommands := []string{ 287 "out/DepFile Flag FlagWithArg=arg FlagWithDepFile=out/depfile FlagWithInput=input FlagWithOutput=out/output Input out/Output Text Tool after command2 old cmd", 288 "command2 out/depfile2 input2 out/output2 tool2", 289 "command3 input3 out/output2 out/output3", 290 } 291 292 wantDepMergerCommand := "out/host/" + ctx.Config().PrebuiltOS() + "/bin/dep_fixer out/DepFile out/depfile out/ImplicitDepFile out/depfile2" 293 294 wantInputs := PathsForSource(ctx, []string{"Implicit", "Input", "input", "input2", "input3"}) 295 wantOutputs := PathsForOutput(ctx, []string{"ImplicitOutput", "Output", "output", "output2", "output3"}) 296 wantDepFiles := PathsForOutput(ctx, []string{"DepFile", "depfile", "ImplicitDepFile", "depfile2"}) 297 wantTools := PathsForSource(ctx, []string{"Tool", "tool2"}) 298 299 if g, w := rule.Commands(), wantCommands; !reflect.DeepEqual(g, w) { 300 t.Errorf("\nwant rule.Commands() = %#v\n got %#v", w, g) 301 } 302 303 if g, w := rule.depFileMergerCmd(ctx, rule.DepFiles()).String(), wantDepMergerCommand; g != w { 304 t.Errorf("\nwant rule.depFileMergerCmd() = %#v\n got %#v", w, g) 305 } 306 307 if g, w := rule.Inputs(), wantInputs; !reflect.DeepEqual(w, g) { 308 t.Errorf("\nwant rule.Inputs() = %#v\n got %#v", w, g) 309 } 310 if g, w := rule.Outputs(), wantOutputs; !reflect.DeepEqual(w, g) { 311 t.Errorf("\nwant rule.Outputs() = %#v\n got %#v", w, g) 312 } 313 if g, w := rule.DepFiles(), wantDepFiles; !reflect.DeepEqual(w, g) { 314 t.Errorf("\nwant rule.DepFiles() = %#v\n got %#v", w, g) 315 } 316 if g, w := rule.Tools(), wantTools; !reflect.DeepEqual(w, g) { 317 t.Errorf("\nwant rule.Tools() = %#v\n got %#v", w, g) 318 } 319} 320 321func testRuleBuilderFactory() Module { 322 module := &testRuleBuilderModule{} 323 module.AddProperties(&module.properties) 324 InitAndroidModule(module) 325 return module 326} 327 328type testRuleBuilderModule struct { 329 ModuleBase 330 properties struct { 331 Src string 332 } 333} 334 335func (t *testRuleBuilderModule) GenerateAndroidBuildActions(ctx ModuleContext) { 336 in := PathForSource(ctx, t.properties.Src) 337 out := PathForModuleOut(ctx, ctx.ModuleName()) 338 339 testRuleBuilder_Build(ctx, in, out) 340} 341 342type testRuleBuilderSingleton struct{} 343 344func testRuleBuilderSingletonFactory() Singleton { 345 return &testRuleBuilderSingleton{} 346} 347 348func (t *testRuleBuilderSingleton) GenerateBuildActions(ctx SingletonContext) { 349 in := PathForSource(ctx, "bar") 350 out := PathForOutput(ctx, "baz") 351 testRuleBuilder_Build(ctx, in, out) 352} 353 354func testRuleBuilder_Build(ctx BuilderContext, in Path, out WritablePath) { 355 rule := NewRuleBuilder() 356 357 rule.Command().Tool(PathForSource(ctx, "cp")).Input(in).Output(out) 358 359 rule.Restat() 360 361 rule.Build(pctx, ctx, "rule", "desc") 362} 363 364func TestRuleBuilder_Build(t *testing.T) { 365 buildDir, err := ioutil.TempDir("", "soong_test_rule_builder") 366 if err != nil { 367 t.Fatal(err) 368 } 369 defer os.RemoveAll(buildDir) 370 371 bp := ` 372 rule_builder_test { 373 name: "foo", 374 src: "bar", 375 } 376 ` 377 378 config := TestConfig(buildDir, nil) 379 ctx := NewTestContext() 380 ctx.MockFileSystem(map[string][]byte{ 381 "Android.bp": []byte(bp), 382 "bar": nil, 383 "cp": nil, 384 }) 385 ctx.RegisterModuleType("rule_builder_test", ModuleFactoryAdaptor(testRuleBuilderFactory)) 386 ctx.RegisterSingletonType("rule_builder_test", SingletonFactoryAdaptor(testRuleBuilderSingletonFactory)) 387 ctx.Register() 388 389 _, errs := ctx.ParseFileList(".", []string{"Android.bp"}) 390 FailIfErrored(t, errs) 391 _, errs = ctx.PrepareBuildActions(config) 392 FailIfErrored(t, errs) 393 394 check := func(t *testing.T, params TestingBuildParams, wantOutput string) { 395 if len(params.RuleParams.CommandDeps) != 1 || params.RuleParams.CommandDeps[0] != "cp" { 396 t.Errorf("want RuleParams.CommandDeps = [%q], got %q", "cp", params.RuleParams.CommandDeps) 397 } 398 399 if len(params.Implicits) != 1 || params.Implicits[0].String() != "bar" { 400 t.Errorf("want Implicits = [%q], got %q", "bar", params.Implicits.Strings()) 401 } 402 403 if params.Output.String() != wantOutput { 404 t.Errorf("want Output = %q, got %q", wantOutput, params.Output) 405 } 406 407 if !params.RuleParams.Restat { 408 t.Errorf("want RuleParams.Restat = true, got %v", params.RuleParams.Restat) 409 } 410 } 411 412 t.Run("module", func(t *testing.T) { 413 check(t, ctx.ModuleForTests("foo", "").Rule("rule"), 414 filepath.Join(buildDir, ".intermediates", "foo", "foo")) 415 }) 416 t.Run("singleton", func(t *testing.T) { 417 check(t, ctx.SingletonForTests("rule_builder_test").Rule("rule"), 418 filepath.Join(buildDir, "baz")) 419 }) 420} 421