aboutsummaryrefslogtreecommitdiffstats
path: root/genrule
diff options
context:
space:
mode:
authorColin Cross <ccross@android.com>2018-10-04 23:28:25 -0700
committerColin Cross <ccross@android.com>2018-10-05 21:29:14 +0000
commit2a0769264397c07693c2948d6df98fc983d8982b (patch)
treef8820ff285998e6f5a58f6dc2a46b9d24b835c2d /genrule
parentecdeb1e7051aacb1866d2648a75c58be318364b2 (diff)
downloadbuild_soong-2a0769264397c07693c2948d6df98fc983d8982b.tar.gz
build_soong-2a0769264397c07693c2948d6df98fc983d8982b.tar.bz2
build_soong-2a0769264397c07693c2948d6df98fc983d8982b.zip
Add tests for genrule command expansion
Add tests for expanding variables in a genrule cmd property. Test: genrule_test.go Change-Id: I8288b8616d518bb5f24a892c4e59f68d95055d0a
Diffstat (limited to 'genrule')
-rw-r--r--genrule/genrule.go6
-rw-r--r--genrule/genrule_test.go374
2 files changed, 378 insertions, 2 deletions
diff --git a/genrule/genrule.go b/genrule/genrule.go
index e3823c5c..f19e2aa0 100644
--- a/genrule/genrule.go
+++ b/genrule/genrule.go
@@ -102,8 +102,9 @@ type Module struct {
taskGenerator taskFunc
- deps android.Paths
- rule blueprint.Rule
+ deps android.Paths
+ rule blueprint.Rule
+ rawCommand string
exportedIncludeDirs android.Paths
@@ -287,6 +288,7 @@ func (g *Module) GenerateAndroidBuildActions(ctx android.ModuleContext) {
genDir := android.PathForModuleGen(ctx)
// Escape the command for the shell
rawCommand = "'" + strings.Replace(rawCommand, "'", `'\''`, -1) + "'"
+ g.rawCommand = rawCommand
sandboxCommand := fmt.Sprintf("$sboxCmd --sandbox-path %s --output-root %s -c %s %s $allouts",
sandboxPath, genDir, rawCommand, depfilePlaceholder)
diff --git a/genrule/genrule_test.go b/genrule/genrule_test.go
new file mode 100644
index 00000000..0d690d42
--- /dev/null
+++ b/genrule/genrule_test.go
@@ -0,0 +1,374 @@
+// Copyright 2018 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package genrule
+
+import (
+ "io/ioutil"
+ "os"
+ "strings"
+ "testing"
+
+ "android/soong/android"
+)
+
+var buildDir string
+
+func setUp() {
+ var err error
+ buildDir, err = ioutil.TempDir("", "soong_java_test")
+ if err != nil {
+ panic(err)
+ }
+}
+
+func tearDown() {
+ os.RemoveAll(buildDir)
+}
+
+func TestMain(m *testing.M) {
+ run := func() int {
+ setUp()
+ defer tearDown()
+
+ return m.Run()
+ }
+
+ os.Exit(run())
+}
+
+func testContext(config android.Config, bp string,
+ fs map[string][]byte) *android.TestContext {
+
+ ctx := android.NewTestArchContext()
+ ctx.RegisterModuleType("filegroup", android.ModuleFactoryAdaptor(android.FileGroupFactory))
+ ctx.RegisterModuleType("genrule", android.ModuleFactoryAdaptor(GenRuleFactory))
+ ctx.RegisterModuleType("tool", android.ModuleFactoryAdaptor(toolFactory))
+ ctx.Register()
+
+ bp += `
+ tool {
+ name: "tool",
+ }
+
+ filegroup {
+ name: "tool_files",
+ srcs: [
+ "tool_file1",
+ "tool_file2",
+ ],
+ }
+
+ filegroup {
+ name: "1tool_file",
+ srcs: [
+ "tool_file1",
+ ],
+ }
+
+ filegroup {
+ name: "ins",
+ srcs: [
+ "in1",
+ "in2",
+ ],
+ }
+
+ filegroup {
+ name: "1in",
+ srcs: [
+ "in1",
+ ],
+ }
+
+ filegroup {
+ name: "empty",
+ }
+ `
+
+ mockFS := map[string][]byte{
+ "Android.bp": []byte(bp),
+ "tool": nil,
+ "tool_file1": nil,
+ "tool_file2": nil,
+ "in1": nil,
+ "in2": nil,
+ }
+
+ for k, v := range fs {
+ mockFS[k] = v
+ }
+
+ ctx.MockFileSystem(mockFS)
+
+ return ctx
+}
+
+func TestGenruleCmd(t *testing.T) {
+ testcases := []struct {
+ name string
+ prop string
+
+ err string
+ expect string
+ }{
+ {
+ name: "empty location tool",
+ prop: `
+ tools: ["tool"],
+ out: ["out"],
+ cmd: "$(location) > $(out)",
+ `,
+ expect: "out/tool > __SBOX_OUT_FILES__",
+ },
+ {
+ name: "empty location tool file",
+ prop: `
+ tool_files: ["tool_file1"],
+ out: ["out"],
+ cmd: "$(location) > $(out)",
+ `,
+ expect: "tool_file1 > __SBOX_OUT_FILES__",
+ },
+ {
+ name: "empty location tool file fg",
+ prop: `
+ tool_files: [":1tool_file"],
+ out: ["out"],
+ cmd: "$(location) > $(out)",
+ `,
+ expect: "tool_file1 > __SBOX_OUT_FILES__",
+ },
+ {
+ name: "empty location tool and tool file",
+ prop: `
+ tools: ["tool"],
+ tool_files: ["tool_file1"],
+ out: ["out"],
+ cmd: "$(location) > $(out)",
+ `,
+ expect: "out/tool > __SBOX_OUT_FILES__",
+ },
+ {
+ name: "tool",
+ prop: `
+ tools: ["tool"],
+ out: ["out"],
+ cmd: "$(location tool) > $(out)",
+ `,
+ expect: "out/tool > __SBOX_OUT_FILES__",
+ },
+ {
+ name: "tool file",
+ prop: `
+ tool_files: ["tool_file1"],
+ out: ["out"],
+ cmd: "$(location tool_file1) > $(out)",
+ `,
+ expect: "tool_file1 > __SBOX_OUT_FILES__",
+ },
+ {
+ name: "tool file fg",
+ prop: `
+ tool_files: [":1tool_file"],
+ out: ["out"],
+ cmd: "$(location tool_file1) > $(out)",
+ `,
+ expect: "tool_file1 > __SBOX_OUT_FILES__",
+ },
+ {
+ name: "tool files",
+ prop: `
+ tool_files: [":tool_files"],
+ out: ["out"],
+ cmd: "$(location tool_file1) $(location tool_file2) > $(out)",
+ `,
+ expect: "tool_file1 tool_file2 > __SBOX_OUT_FILES__",
+ },
+ {
+ name: "in1",
+ prop: `
+ srcs: ["in1"],
+ out: ["out"],
+ cmd: "cat $(in) > $(out)",
+ `,
+ expect: "cat ${in} > __SBOX_OUT_FILES__",
+ },
+ {
+ name: "in1 fg",
+ prop: `
+ srcs: [":1in"],
+ out: ["out"],
+ cmd: "cat $(in) > $(out)",
+ `,
+ expect: "cat ${in} > __SBOX_OUT_FILES__",
+ },
+ {
+ name: "ins",
+ prop: `
+ srcs: ["in1", "in2"],
+ out: ["out"],
+ cmd: "cat $(in) > $(out)",
+ `,
+ expect: "cat ${in} > __SBOX_OUT_FILES__",
+ },
+ {
+ name: "ins fg",
+ prop: `
+ srcs: [":ins"],
+ out: ["out"],
+ cmd: "cat $(in) > $(out)",
+ `,
+ expect: "cat ${in} > __SBOX_OUT_FILES__",
+ },
+ {
+ name: "outs",
+ prop: `
+ out: ["out", "out2"],
+ cmd: "echo foo > $(out)",
+ `,
+ expect: "echo foo > __SBOX_OUT_FILES__",
+ },
+ {
+ name: "depfile",
+ prop: `
+ out: ["out"],
+ depfile: true,
+ cmd: "echo foo > $(out) && touch $(depfile)",
+ `,
+ expect: "echo foo > __SBOX_OUT_FILES__ && touch __SBOX_DEPFILE__",
+ },
+ {
+ name: "gendir",
+ prop: `
+ out: ["out"],
+ cmd: "echo foo > $(genDir)/foo && cp $(genDir)/foo $(out)",
+ `,
+ expect: "echo foo > __SBOX_OUT_DIR__/foo && cp __SBOX_OUT_DIR__/foo __SBOX_OUT_FILES__",
+ },
+
+ {
+ name: "error empty location",
+ prop: `
+ out: ["out"],
+ cmd: "$(location) > $(out)",
+ `,
+ err: "at least one `tools` or `tool_files` is required if $(location) is used",
+ },
+ {
+ name: "error location",
+ prop: `
+ out: ["out"],
+ cmd: "echo foo > $(location missing)",
+ `,
+ err: `unknown location label "missing"`,
+ },
+ {
+ name: "error variable",
+ prop: `
+ out: ["out"],
+ srcs: ["in1"],
+ cmd: "echo $(foo) > $(out)",
+ `,
+ err: `unknown variable '$(foo)'`,
+ },
+ {
+ name: "error depfile",
+ prop: `
+ out: ["out"],
+ cmd: "echo foo > $(out) && touch $(depfile)",
+ `,
+ err: "$(depfile) used without depfile property",
+ },
+ {
+ name: "error no depfile",
+ prop: `
+ out: ["out"],
+ depfile: true,
+ cmd: "echo foo > $(out)",
+ `,
+ err: "specified depfile=true but did not include a reference to '${depfile}' in cmd",
+ },
+ {
+ name: "error no out",
+ prop: `
+ cmd: "echo foo > $(out)",
+ `,
+ err: "must have at least one output file",
+ },
+ }
+
+ for _, test := range testcases {
+ t.Run(test.name, func(t *testing.T) {
+ config := android.TestArchConfig(buildDir, nil)
+ bp := "genrule {\n"
+ bp += "name: \"gen\",\n"
+ bp += test.prop
+ bp += "}\n"
+
+ ctx := testContext(config, bp, nil)
+
+ _, errs := ctx.ParseFileList(".", []string{"Android.bp"})
+ if errs == nil {
+ _, errs = ctx.PrepareBuildActions(config)
+ }
+ if errs == nil && test.err != "" {
+ t.Fatalf("want error %q, got no error", test.err)
+ } else if errs != nil && test.err == "" {
+ android.FailIfErrored(t, errs)
+ } else if test.err != "" {
+ if len(errs) != 1 {
+ t.Errorf("want 1 error, got %d errors:", len(errs))
+ for _, err := range errs {
+ t.Errorf(" %s", err.Error())
+ }
+ t.FailNow()
+ }
+ if !strings.Contains(errs[0].Error(), test.err) {
+ t.Fatalf("want %q, got %q", test.err, errs[0].Error())
+ }
+ return
+ }
+
+ gen := ctx.ModuleForTests("gen", "").Module().(*Module)
+ if gen.rawCommand != "'"+test.expect+"'" {
+ t.Errorf("want %q, got %q", test.expect, gen.rawCommand)
+ }
+ })
+ }
+
+}
+
+type testTool struct {
+ android.ModuleBase
+ outputFile android.Path
+}
+
+func toolFactory() android.Module {
+ module := &testTool{}
+ android.InitAndroidArchModule(module, android.HostSupported, android.MultilibFirst)
+ return module
+}
+
+func (t *testTool) DepsMutator(ctx android.BottomUpMutatorContext) {}
+
+func (t *testTool) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ t.outputFile = android.PathForTesting("out", ctx.ModuleName())
+}
+
+func (t *testTool) HostToolPath() android.OptionalPath {
+ return android.OptionalPathForPath(t.outputFile)
+}
+
+var _ HostToolProvider = (*testTool)(nil)