• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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