Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  genrule_test.go   Sprache: unbekannt

 
Spracherkennung für: .go vermutete Sprache: Unknown {[0] [0] [0]} [Methode: Schwerpunktbildung, einfache Gewichte, sechs Dimensionen]

// 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 (
 "fmt"
 "os"
 "regexp"
 "strconv"
 "testing"

 "android/soong/android"

 "github.com/google/blueprint/proptools"
)

func TestMain(m *testing.M) {
 os.Exit(m.Run())
}

var prepareForGenRuleTest = android.GroupFixturePreparers(
 android.PrepareForTestWithArchMutator,
 android.PrepareForTestWithDefaults,
 android.PrepareForTestWithFilegroup,
 PrepareForTestWithGenRuleBuildComponents,
 android.FixtureRegisterWithContext(func(ctx android.RegistrationContext) {
  android.RegisterPrebuiltMutators(ctx)
  ctx.RegisterModuleType("tool", toolFactory)
  ctx.RegisterModuleType("prebuilt_tool", prebuiltToolFactory)
  ctx.RegisterModuleType("output", outputProducerFactory)
  ctx.RegisterModuleType("use_source", useSourceFactory)
 }),
 android.FixtureMergeMockFs(android.MockFS{
  "tool":       nil,
  "tool_file1": nil,
  "tool_file2": nil,
  "in1":        nil,
  "in2":        nil,
  "in1.txt":    nil,
  "in2.txt":    nil,
  "in3.txt":    nil,
 }),
)

func testGenruleBp() string {
 return `
  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",
  }
 `
}

func TestGenruleCmd(t *testing.T) {
 testcases := []struct {
  name       string
  moduleName string
  prop       string

  allowMissingDependencies bool

  err    string
  expect string
 }{
  {
   name: "empty location tool",
   prop: `
    tools: ["tool"],
    out: ["out"],
    cmd: "$(location) > $(out)",
   `,
   expect: "__SBOX_SANDBOX_DIR__/tools/out/bin/tool > __SBOX_SANDBOX_DIR__/out/out",
  },
  {
   name: "empty location tool2",
   prop: `
    tools: [":tool"],
    out: ["out"],
    cmd: "$(location) > $(out)",
   `,
   expect: "__SBOX_SANDBOX_DIR__/tools/out/bin/tool > __SBOX_SANDBOX_DIR__/out/out",
  },
  {
   name: "empty location tool file",
   prop: `
    tool_files: ["tool_file1"],
    out: ["out"],
    cmd: "$(location) > $(out)",
   `,
   expect: "__SBOX_SANDBOX_DIR__/tools/src/tool_file1 > __SBOX_SANDBOX_DIR__/out/out",
  },
  {
   name: "empty location tool file fg",
   prop: `
    tool_files: [":1tool_file"],
    out: ["out"],
    cmd: "$(location) > $(out)",
   `,
   expect: "__SBOX_SANDBOX_DIR__/tools/src/tool_file1 > __SBOX_SANDBOX_DIR__/out/out",
  },
  {
   name: "empty location tool and tool file",
   prop: `
    tools: ["tool"],
    tool_files: ["tool_file1"],
    out: ["out"],
    cmd: "$(location) > $(out)",
   `,
   expect: "__SBOX_SANDBOX_DIR__/tools/out/bin/tool > __SBOX_SANDBOX_DIR__/out/out",
  },
  {
   name: "tool",
   prop: `
    tools: ["tool"],
    out: ["out"],
    cmd: "$(location tool) > $(out)",
   `,
   expect: "__SBOX_SANDBOX_DIR__/tools/out/bin/tool > __SBOX_SANDBOX_DIR__/out/out",
  },
  {
   name: "tool2",
   prop: `
    tools: [":tool"],
    out: ["out"],
    cmd: "$(location :tool) > $(out)",
   `,
   expect: "__SBOX_SANDBOX_DIR__/tools/out/bin/tool > __SBOX_SANDBOX_DIR__/out/out",
  },
  {
   name: "tool file",
   prop: `
    tool_files: ["tool_file1"],
    out: ["out"],
    cmd: "$(location tool_file1) > $(out)",
   `,
   expect: "__SBOX_SANDBOX_DIR__/tools/src/tool_file1 > __SBOX_SANDBOX_DIR__/out/out",
  },
  {
   name: "tool file fg",
   prop: `
    tool_files: [":1tool_file"],
    out: ["out"],
    cmd: "$(location :1tool_file) > $(out)",
   `,
   expect: "__SBOX_SANDBOX_DIR__/tools/src/tool_file1 > __SBOX_SANDBOX_DIR__/out/out",
  },
  {
   name: "tool files",
   prop: `
    tool_files: [":tool_files"],
    out: ["out"],
    cmd: "$(locations :tool_files) > $(out)",
   `,
   expect: "__SBOX_SANDBOX_DIR__/tools/src/tool_file1 __SBOX_SANDBOX_DIR__/tools/src/tool_file2 > __SBOX_SANDBOX_DIR__/out/out",
  },
  {
   name: "in1",
   prop: `
    srcs: ["in1"],
    out: ["out"],
    cmd: "cat $(in) > $(out)",
   `,
   expect: "cat in1 > __SBOX_SANDBOX_DIR__/out/out",
  },
  {
   name: "in1 fg",
   prop: `
    srcs: [":1in"],
    out: ["out"],
    cmd: "cat $(in) > $(out)",
   `,
   expect: "cat in1 > __SBOX_SANDBOX_DIR__/out/out",
  },
  {
   name: "ins",
   prop: `
    srcs: ["in1", "in2"],
    out: ["out"],
    cmd: "cat $(in) > $(out)",
   `,
   expect: "cat in1 in2 > __SBOX_SANDBOX_DIR__/out/out",
  },
  {
   name: "ins fg",
   prop: `
    srcs: [":ins"],
    out: ["out"],
    cmd: "cat $(in) > $(out)",
   `,
   expect: "cat in1 in2 > __SBOX_SANDBOX_DIR__/out/out",
  },
  {
   name: "location in1",
   prop: `
    srcs: ["in1"],
    out: ["out"],
    cmd: "cat $(location in1) > $(out)",
   `,
   expect: "cat in1 > __SBOX_SANDBOX_DIR__/out/out",
  },
  {
   name: "location in1 fg",
   prop: `
    srcs: [":1in"],
    out: ["out"],
    cmd: "cat $(location :1in) > $(out)",
   `,
   expect: "cat in1 > __SBOX_SANDBOX_DIR__/out/out",
  },
  {
   name: "location ins",
   prop: `
    srcs: ["in1", "in2"],
    out: ["out"],
    cmd: "cat $(location in1) > $(out)",
   `,
   expect: "cat in1 > __SBOX_SANDBOX_DIR__/out/out",
  },
  {
   name: "location ins fg",
   prop: `
    srcs: [":ins"],
    out: ["out"],
    cmd: "cat $(locations :ins) > $(out)",
   `,
   expect: "cat in1 in2 > __SBOX_SANDBOX_DIR__/out/out",
  },
  {
   name: "outs",
   prop: `
    out: ["out", "out2"],
    cmd: "echo foo > $(out)",
   `,
   expect: "echo foo > __SBOX_SANDBOX_DIR__/out/out __SBOX_SANDBOX_DIR__/out/out2",
  },
  {
   name: "location out",
   prop: `
    out: ["out", "out2"],
    cmd: "echo foo > $(location out2)",
   `,
   expect: "echo foo > __SBOX_SANDBOX_DIR__/out/out2",
  },
  {
   name: "gendir",
   prop: `
    out: ["out"],
    cmd: "echo foo > $(genDir)/foo && cp $(genDir)/foo $(out)",
   `,
   expect: "echo foo > __SBOX_SANDBOX_DIR__/out/foo && cp __SBOX_SANDBOX_DIR__/out/foo __SBOX_SANDBOX_DIR__/out/out",
  },
  {
   name: "$",
   prop: `
    out: ["out"],
    cmd: "echo $$ > $(out)",
   `,
   expect: "echo $ > __SBOX_SANDBOX_DIR__/out/out",
  },

  {
   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 empty location no files",
   prop: `
    tool_files: [":empty"],
    out: ["out"],
    cmd: "$(location) > $(out)",
   `,
   err: `default label ":empty" has no files`,
  },
  {
   name: "error empty location multiple files",
   prop: `
    tool_files: [":tool_files"],
    out: ["out"],
    cmd: "$(location) > $(out)",
   `,
   err: `default label ":tool_files" has multiple files`,
  },
  {
   name: "error location",
   prop: `
    out: ["out"],
    cmd: "echo foo > $(location missing)",
   `,
   err: `unknown location label "missing" is not in srcs, out, tools or tool_files.`,
  },
  {
   name: "error locations",
   prop: `
     out: ["out"],
     cmd: "echo foo > $(locations missing)",
   `,
   err: `unknown locations label "missing" is not in srcs, out, tools or tool_files`,
  },
  {
   name: "error location no files",
   prop: `
     out: ["out"],
     srcs: [":empty"],
     cmd: "echo $(location :empty) > $(out)",
   `,
   err: `label ":empty" has no files`,
  },
  {
   name: "error locations no files",
   prop: `
     out: ["out"],
     srcs: [":empty"],
     cmd: "echo $(locations :empty) > $(out)",
   `,
   err: `label ":empty" has no files`,
  },
  {
   name: "error location multiple files",
   prop: `
     out: ["out"],
     srcs: [":ins"],
     cmd: "echo $(location :ins) > $(out)",
   `,
   err: `label ":ins" has multiple files`,
  },
  {
   name: "error variable",
   prop: `
     out: ["out"],
     srcs: ["in1"],
     cmd: "echo $(foo) > $(out)",
   `,
   err: `unknown variable '$(foo)'`,
  },
  {
   name: "error no out",
   prop: `
    cmd: "echo foo > $(out)",
   `,
   err: "must have at least one output file",
  },
  {
   name: "srcs allow missing dependencies",
   prop: `
    srcs: [":missing"],
    out: ["out"],
    cmd: "cat $(location :missing) > $(out)",
   `,

   allowMissingDependencies: true,

   expect: "cat '***missing srcs :missing***' > __SBOX_SANDBOX_DIR__/out/out",
  },
  {
   name: "tool allow missing dependencies",
   prop: `
    tools: [":missing"],
    out: ["out"],
    cmd: "$(location :missing) > $(out)",
   `,

   allowMissingDependencies: true,

   expect: "'***missing tool :missing***' > __SBOX_SANDBOX_DIR__/out/out",
  },
 }

 for _, test := range testcases {
  t.Run(test.name, func(t *testing.T) {
   moduleName := "gen"
   if test.moduleName != "" {
    moduleName = test.moduleName
   }
   bp := fmt.Sprintf(`
   genrule {
      name: "%s",
      %s
   }`, moduleName, test.prop)
   var expectedErrors []string
   if test.err != "" {
    expectedErrors = append(expectedErrors, regexp.QuoteMeta(test.err))
   }

   result := android.GroupFixturePreparers(
    prepareForGenRuleTest,
    android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
     variables.Allow_missing_dependencies = proptools.BoolPtr(test.allowMissingDependencies)
    }),
    android.FixtureModifyContext(func(ctx *android.TestContext) {
     ctx.SetAllowMissingDependencies(test.allowMissingDependencies)
    }),
   ).
    ExtendWithErrorHandler(android.FixtureExpectsAllErrorsToMatchAPattern(expectedErrors)).
    RunTestWithBp(t, testGenruleBp()+bp)

   if expectedErrors != nil {
    return
   }

   gen := result.Module(moduleName, "").(*Module)
   android.AssertStringEquals(t, "raw commands", test.expect, gen.rawCommands[0])
  })
 }
}

func TestGenruleHashInputs(t *testing.T) {

 // The basic idea here is to verify that the sbox command (which is
 // in the Command field of the generate rule) contains a hash of the
 // inputs, but only if $(in) is not referenced in the genrule cmd
 // property.

 // By including a hash of the inputs, we cause the rule to re-run if
 // the list of inputs changes (because the sbox command changes).

 // However, if the genrule cmd property already contains $(in), then
 // the dependency is already expressed, so we don't need to include the
 // hash in that case.

 bp := `
   genrule {
    name: "hash0",
    srcs: ["in1.txt", "in2.txt"],
    out: ["out"],
    cmd: "echo foo > $(out)",
   }
   genrule {
    name: "hash1",
    srcs: ["*.txt"],
    out: ["out"],
    cmd: "echo bar > $(out)",
   }
   genrule {
    name: "hash2",
    srcs: ["*.txt"],
    out: ["out"],
    cmd: "echo $(in) > $(out)",
   }
  `
 testcases := []struct {
  name         string
  expectedHash string
 }{
  {
   name: "hash0",
   // sha256 value obtained from: echo -en 'in1.txt\nin2.txt' | sha256sum
   expectedHash: "18da75b9b1cc74b09e365b4ca2e321b5d618f438cc632b387ad9dc2ab4b20e9d",
  },
  {
   name: "hash1",
   // sha256 value obtained from: echo -en 'in1.txt\nin2.txt\nin3.txt' | sha256sum
   expectedHash: "a38d432a4b19df93140e1f1fe26c97ff0387dae01fe506412b47208f0595fb45",
  },
  {
   name: "hash2",
   // sha256 value obtained from: echo -en 'in1.txt\nin2.txt\nin3.txt' | sha256sum
   expectedHash: "a38d432a4b19df93140e1f1fe26c97ff0387dae01fe506412b47208f0595fb45",
  },
 }

 result := prepareForGenRuleTest.RunTestWithBp(t, testGenruleBp()+bp)

 for _, test := range testcases {
  t.Run(test.name, func(t *testing.T) {
   gen := result.ModuleForTests(t, test.name, "")
   manifest := android.RuleBuilderSboxProtoForTests(t, result.TestContext, gen.Output("genrule.sbox.textproto"))
   hash := manifest.Commands[0].GetInputHash()

   android.AssertStringEquals(t, "hash", test.expectedHash, hash)
  })
 }
}

func TestGenSrcs(t *testing.T) {
 testcases := []struct {
  name string
  prop string

  allowMissingDependencies bool

  err    string
  cmds   []string
  deps   []string
  files  []string
  shards int
  inputs []string
 }{
  {
   name: "gensrcs",
   prop: `
    tools: ["tool"],
    srcs: ["in1.txt", "in2.txt"],
    cmd: "$(location) $(in) > $(out)",
   `,
   cmds: []string{
    "bash -c '__SBOX_SANDBOX_DIR__/tools/out/bin/tool in1.txt > __SBOX_SANDBOX_DIR__/out/in1.h' && bash -c '__SBOX_SANDBOX_DIR__/tools/out/bin/tool in2.txt > __SBOX_SANDBOX_DIR__/out/in2.h'",
   },
   deps: []string{
    "out/soong/.intermediates/gen/gen/gensrcs/in1.h",
    "out/soong/.intermediates/gen/gen/gensrcs/in2.h",
   },
   files: []string{
    "out/soong/.intermediates/gen/gen/gensrcs/in1.h",
    "out/soong/.intermediates/gen/gen/gensrcs/in2.h",
   },
  },
  {
   name: "shards",
   prop: `
    tools: ["tool"],
    srcs: ["in1.txt", "in2.txt", "in3.txt"],
    cmd: "$(location) $(in) > $(out)",
    shard_size: 2,
   `,
   cmds: []string{
    "bash -c '__SBOX_SANDBOX_DIR__/tools/out/bin/tool in1.txt > __SBOX_SANDBOX_DIR__/out/in1.h' && bash -c '__SBOX_SANDBOX_DIR__/tools/out/bin/tool in2.txt > __SBOX_SANDBOX_DIR__/out/in2.h'",
    "bash -c '__SBOX_SANDBOX_DIR__/tools/out/bin/tool in3.txt > __SBOX_SANDBOX_DIR__/out/in3.h'",
   },
   deps: []string{
    "out/soong/.intermediates/gen/gen/gensrcs/in1.h",
    "out/soong/.intermediates/gen/gen/gensrcs/in2.h",
    "out/soong/.intermediates/gen/gen/gensrcs/in3.h",
   },
   files: []string{
    "out/soong/.intermediates/gen/gen/gensrcs/in1.h",
    "out/soong/.intermediates/gen/gen/gensrcs/in2.h",
    "out/soong/.intermediates/gen/gen/gensrcs/in3.h",
   },
  },
  {
   name: "data",
   prop: `
    tools: ["tool"],
    srcs: ["in1.txt", "in2.txt", "in3.txt"],
    cmd: "$(location) $(in) --extra_input=$(location baz.txt) > $(out)",
    data: ["baz.txt"],
    shard_size: 2,
   `,
   cmds: []string{
    "bash -c '__SBOX_SANDBOX_DIR__/tools/out/bin/tool in1.txt --extra_input=baz.txt > __SBOX_SANDBOX_DIR__/out/in1.h' && bash -c '__SBOX_SANDBOX_DIR__/tools/out/bin/tool in2.txt --extra_input=baz.txt > __SBOX_SANDBOX_DIR__/out/in2.h'",
    "bash -c '__SBOX_SANDBOX_DIR__/tools/out/bin/tool in3.txt --extra_input=baz.txt > __SBOX_SANDBOX_DIR__/out/in3.h'",
   },
   deps: []string{
    "out/soong/.intermediates/gen/gen/gensrcs/in1.h",
    "out/soong/.intermediates/gen/gen/gensrcs/in2.h",
    "out/soong/.intermediates/gen/gen/gensrcs/in3.h",
   },
   files: []string{
    "out/soong/.intermediates/gen/gen/gensrcs/in1.h",
    "out/soong/.intermediates/gen/gen/gensrcs/in2.h",
    "out/soong/.intermediates/gen/gen/gensrcs/in3.h",
   },
   shards: 2,
   inputs: []string{
    "baz.txt",
   },
  },
 }

 checkInputs := func(t *testing.T, rule android.TestingBuildParams, inputs []string) {
  t.Helper()
  if len(inputs) == 0 {
   return
  }
  inputBaseNames := map[string]bool{}
  for _, f := range rule.Implicits {
   inputBaseNames[f.Base()] = true
  }
  for _, f := range inputs {
   if _, ok := inputBaseNames[f]; !ok {
    t.Errorf("Expected to find input file %q for %q, but did not", f, rule.Description)
   }
  }
 }

 for _, test := range testcases {
  t.Run(test.name, func(t *testing.T) {
   bp := "gensrcs {\n"
   bp += `name: "gen",` + "\n"
   bp += `output_extension: "h",` + "\n"
   bp += test.prop
   bp += "}\n"

   var expectedErrors []string
   if test.err != "" {
    expectedErrors = append(expectedErrors, regexp.QuoteMeta(test.err))
   }

   result := prepareForGenRuleTest.
    ExtendWithErrorHandler(android.FixtureExpectsAllErrorsToMatchAPattern(expectedErrors)).
    RunTestWithBp(t, testGenruleBp()+bp)

   mod := result.ModuleForTests(t, "gen", "")
   if expectedErrors != nil {
    return
   }

   if test.shards > 0 {
    for i := 0; i < test.shards; i++ {
     r := mod.Rule("generator" + strconv.Itoa(i))
     checkInputs(t, r, test.inputs)
    }
   } else {
    r := mod.Rule("generator")
    checkInputs(t, r, test.inputs)
   }

   gen := result.Module("gen", "").(*Module)
   android.AssertDeepEquals(t, "cmd", test.cmds, gen.rawCommands)

   android.AssertPathsRelativeToTopEquals(t, "deps", test.deps, gen.outputDeps)

   android.AssertPathsRelativeToTopEquals(t, "files", test.files, gen.outputFiles)
  })
 }
}

func TestGenruleDefaults(t *testing.T) {
 bp := `
    genrule_defaults {
     name: "gen_defaults1",
     cmd: "cp $(in) $(out)",
    }

    genrule_defaults {
     name: "gen_defaults2",
     srcs: ["in1"],
    }

    genrule {
     name: "gen",
     out: ["out"],
     defaults: ["gen_defaults1", "gen_defaults2"],
    }
   `

 result := prepareForGenRuleTest.RunTestWithBp(t, testGenruleBp()+bp)

 gen := result.Module("gen", "").(*Module)

 expectedCmd := "cp in1 __SBOX_SANDBOX_DIR__/out/out"
 android.AssertStringEquals(t, "cmd", expectedCmd, gen.rawCommands[0])
}

func TestGenruleAllowMissingDependencies(t *testing.T) {
 bp := `
  output {
   name: "disabled",
   enabled: false,
  }

  genrule {
   name: "gen",
   srcs: [
    ":disabled",
   ],
   out: ["out"],
   cmd: "cat $(in) > $(out)",
  }
       `
 result := android.GroupFixturePreparers(
  prepareForGenRuleTest,
  android.FixtureModifyConfigAndContext(
   func(config android.Config, ctx *android.TestContext) {
    config.TestProductVariables.Allow_missing_dependencies = proptools.BoolPtr(true)
    ctx.SetAllowMissingDependencies(true)
   })).RunTestWithBp(t, bp)

 gen := result.ModuleForTests(t, "gen", "").Output("out")
 if !android.IsErrorRule(gen.Rule) {
  t.Errorf("Expected missing dependency error rule for gen, got %q", gen.Rule.String())
 }
}

func TestGenruleOutputFiles(t *testing.T) {
 bp := `
    genrule {
     name: "gen",
     out: ["foo", "sub/bar"],
     cmd: "echo foo > $(location foo) && echo bar > $(location sub/bar)",
    }
    use_source {
     name: "gen_foo",
     srcs: [":gen{foo}"],
    }
    use_source {
     name: "gen_bar",
     srcs: [":gen{sub/bar}"],
    }
    use_source {
     name: "gen_all",
     srcs: [":gen"],
    }
   `

 result := prepareForGenRuleTest.RunTestWithBp(t, testGenruleBp()+bp)
 android.AssertPathsRelativeToTopEquals(t,
  "genrule.tag with output",
  []string{"out/soong/.intermediates/gen/gen/foo"},
  result.ModuleForTests(t, "gen_foo", "").Module().(*useSource).srcs)
 android.AssertPathsRelativeToTopEquals(t,
  "genrule.tag with output in subdir",
  []string{"out/soong/.intermediates/gen/gen/sub/bar"},
  result.ModuleForTests(t, "gen_bar", "").Module().(*useSource).srcs)
 android.AssertPathsRelativeToTopEquals(t,
  "genrule.tag with all",
  []string{"out/soong/.intermediates/gen/gen/foo", "out/soong/.intermediates/gen/gen/sub/bar"},
  result.ModuleForTests(t, "gen_all", "").Module().(*useSource).srcs)
}

func TestGenruleInterface(t *testing.T) {
 result := android.GroupFixturePreparers(
  prepareForGenRuleTest,
  android.FixtureMergeMockFs(android.MockFS{
   "package-dir/Android.bp": []byte(`
    genrule {
     name: "module-name",
     cmd: "mkdir -p $(genDir) && cat $(in) >> $(genDir)/$(out)",
     srcs: [
      "src/foo.proto",
     ],
     out: ["proto.h", "bar/proto.h"],
     export_include_dirs: [".", "bar"],
    }
   `),
  }),
 ).RunTest(t)

 exportedIncludeDirs := []string{
  "out/soong/.intermediates/package-dir/module-name/gen/package-dir",
  "out/soong/.intermediates/package-dir/module-name/gen",
  "out/soong/.intermediates/package-dir/module-name/gen/package-dir/bar",
  "out/soong/.intermediates/package-dir/module-name/gen/bar",
 }
 gen := result.Module("module-name", "").(*Module)

 android.AssertPathsRelativeToTopEquals(
  t,
  "include path",
  exportedIncludeDirs,
  gen.GeneratedHeaderDirs(),
 )
 android.AssertPathsRelativeToTopEquals(
  t,
  "files",
  []string{
   "out/soong/.intermediates/package-dir/module-name/gen/proto.h",
   "out/soong/.intermediates/package-dir/module-name/gen/bar/proto.h",
  },
  gen.GeneratedSourceFiles(),
 )
}

func TestGenSrcsWithNonRootAndroidBpOutputFiles(t *testing.T) {
 result := android.GroupFixturePreparers(
  prepareForGenRuleTest,
  android.FixtureMergeMockFs(android.MockFS{
   "external-protos/path/Android.bp": []byte(`
    filegroup {
     name: "external-protos",
     srcs: ["baz/baz.proto", "bar.proto"],
    }
   `),
   "package-dir/Android.bp": []byte(`
    gensrcs {
     name: "module-name",
     cmd: "mkdir -p $(genDir) && cat $(in) >> $(genDir)/$(out)",
     srcs: [
      "src/foo.proto",
      ":external-protos",
     ],
     output_extension: "proto.h",
    }
   `),
  }),
 ).RunTest(t)

 exportedIncludeDir := "out/soong/.intermediates/package-dir/module-name/gen/gensrcs"
 gen := result.Module("module-name", "").(*Module)

 android.AssertPathsRelativeToTopEquals(
  t,
  "include path",
  []string{exportedIncludeDir},
  gen.exportedIncludeDirs,
 )
 android.AssertPathsRelativeToTopEquals(
  t,
  "files",
  []string{
   exportedIncludeDir + "/package-dir/src/foo.proto.h",
   exportedIncludeDir + "/external-protos/path/baz/baz.proto.h",
   exportedIncludeDir + "/external-protos/path/bar.proto.h",
  },
  gen.outputFiles,
 )
}

func TestGenSrcsWithSrcsFromExternalPackage(t *testing.T) {
 bp := `
  gensrcs {
   name: "module-name",
   cmd: "mkdir -p $(genDir) && cat $(in) >> $(genDir)/$(out)",
   srcs: [
    ":external-protos",
   ],
   output_extension: "proto.h",
  }
 `
 result := android.GroupFixturePreparers(
  prepareForGenRuleTest,
  android.FixtureMergeMockFs(android.MockFS{
   "external-protos/path/Android.bp": []byte(`
    filegroup {
     name: "external-protos",
     srcs: ["foo/foo.proto", "bar.proto"],
    }
   `),
  }),
 ).RunTestWithBp(t, bp)

 exportedIncludeDir := "out/soong/.intermediates/module-name/gen/gensrcs"
 gen := result.Module("module-name", "").(*Module)

 android.AssertPathsRelativeToTopEquals(
  t,
  "include path",
  []string{exportedIncludeDir},
  gen.exportedIncludeDirs,
 )
 android.AssertPathsRelativeToTopEquals(
  t,
  "files",
  []string{
   exportedIncludeDir + "/external-protos/path/foo/foo.proto.h",
   exportedIncludeDir + "/external-protos/path/bar.proto.h",
  },
  gen.outputFiles,
 )
}

func TestGenSrcsWithOutpuExtension(t *testing.T) {
 result := android.GroupFixturePreparers(
  prepareForGenRuleTest,
  android.FixtureMergeMockFs(android.MockFS{
   "external-protos/path/Android.bp": []byte(`
    filegroup {
     name: "external-protos",
     srcs: ["baz/baz.a.b.c.proto", "bar.a.b.c.proto"],
    }
   `),
   "package-dir/Android.bp": []byte(`
    gensrcs {
     name: "module-name",
     cmd: "mkdir -p $(genDir) && cat $(in) >> $(genDir)/$(out)",
     srcs: [
      "src/foo.a.b.c.proto",
      ":external-protos",
     ],

     output_extension: "proto.h",
    }
   `),
  }),
 ).RunTest(t)

 exportedIncludeDir := "out/soong/.intermediates/package-dir/module-name/gen/gensrcs"
 gen := result.Module("module-name", "").(*Module)

 android.AssertPathsRelativeToTopEquals(
  t,
  "include path",
  []string{exportedIncludeDir},
  gen.exportedIncludeDirs,
 )
 android.AssertPathsRelativeToTopEquals(
  t,
  "files",
  []string{
   exportedIncludeDir + "/package-dir/src/foo.a.b.c.proto.h",
   exportedIncludeDir + "/external-protos/path/baz/baz.a.b.c.proto.h",
   exportedIncludeDir + "/external-protos/path/bar.a.b.c.proto.h",
  },
  gen.outputFiles,
 )
}

func TestPrebuiltTool(t *testing.T) {
 testcases := []struct {
  name             string
  bp               string
  expectedToolName string
 }{
  {
   name: "source only",
   bp: `
    tool { name: "tool" }
   `,
   expectedToolName: "bin/tool",
  },
  {
   name: "prebuilt only",
   bp: `
    prebuilt_tool { name: "tool" }
   `,
   expectedToolName: "prebuilt_bin/tool",
  },
  {
   name: "source preferred",
   bp: `
    tool { name: "tool" }
    prebuilt_tool { name: "tool" }
   `,
   expectedToolName: "bin/tool",
  },
  {
   name: "prebuilt preferred",
   bp: `
    tool { name: "tool" }
    prebuilt_tool { name: "tool", prefer: true }
   `,
   expectedToolName: "prebuilt_bin/prebuilt_tool",
  },
  {
   name: "source disabled",
   bp: `
    tool { name: "tool", enabled: false }
    prebuilt_tool { name: "tool" }
      `,
   expectedToolName: "prebuilt_bin/prebuilt_tool",
  },
 }

 for _, test := range testcases {
  t.Run(test.name, func(t *testing.T) {
   result := prepareForGenRuleTest.RunTestWithBp(t, test.bp+`
    genrule {
     name: "gen",
     tools: ["tool"],
     out: ["foo"],
     cmd: "$(location tool)",
    }
   `)
   gen := result.Module("gen", "").(*Module)
   expectedCmd := "__SBOX_SANDBOX_DIR__/tools/out/" + test.expectedToolName
   android.AssertStringEquals(t, "command", expectedCmd, gen.rawCommands[0])
  })
 }
}

func TestGenruleWithGlobPaths(t *testing.T) {
 testcases := []struct {
  name            string
  bp              string
  additionalFiles android.MockFS
  expectedCmd     string
 }{
  {
   name: "single file in directory with $ sign",
   bp: `
    genrule {
     name: "gen",
     srcs: ["inn*.txt"],
     out: ["out.txt"],
     cmd: "cp $(in) $(out)",
    }
    `,
   additionalFiles: android.MockFS{"inn$1.txt": nil},
   expectedCmd:     "cp 'inn$1.txt' __SBOX_SANDBOX_DIR__/out/out.txt",
  },
  {
   name: "multiple file in directory with $ sign",
   bp: `
    genrule {
     name: "gen",
     srcs: ["inn*.txt"],
     out: ["."],
     cmd: "cp $(in) $(out)",
    }
    `,
   additionalFiles: android.MockFS{"inn$1.txt": nil, "inn$2.txt": nil},
   expectedCmd:     "cp 'inn$1.txt' 'inn$2.txt' __SBOX_SANDBOX_DIR__/out",
  },
  {
   name: "file in directory with other shell unsafe character",
   bp: `
    genrule {
     name: "gen",
     srcs: ["inn*.txt"],
     out: ["out.txt"],
     cmd: "cp $(in) $(out)",
    }
    `,
   additionalFiles: android.MockFS{"inn@1.txt": nil},
   expectedCmd:     "cp 'inn@1.txt' __SBOX_SANDBOX_DIR__/out/out.txt",
  },
  {
   name: "glob location param with filepath containing $",
   bp: `
    genrule {
     name: "gen",
     srcs: ["**/inn*"],
     out: ["."],
     cmd: "cp $(in) $(location **/inn*)",
    }
    `,
   additionalFiles: android.MockFS{"a/inn$1.txt": nil},
   expectedCmd:     "cp 'a/inn$1.txt' 'a/inn$1.txt'",
  },
  {
   name: "glob locations param with filepath containing $",
   bp: `
    genrule {
     name: "gen",
     tool_files: ["**/inn*"],
     out: ["out.txt"],
     cmd: "cp $(locations  **/inn*) $(out)",
    }
    `,
   additionalFiles: android.MockFS{"a/inn$1.txt": nil},
   expectedCmd:     "cp '__SBOX_SANDBOX_DIR__/tools/src/a/inn$1.txt' __SBOX_SANDBOX_DIR__/out/out.txt",
  },
 }

 for _, test := range testcases {
  t.Run(test.name, func(t *testing.T) {
   result := android.GroupFixturePreparers(
    prepareForGenRuleTest,
    android.FixtureMergeMockFs(test.additionalFiles),
   ).RunTestWithBp(t, test.bp)
   gen := result.Module("gen", "").(*Module)
   android.AssertStringEquals(t, "command", test.expectedCmd, gen.rawCommands[0])
  })
 }
}

func TestGenruleUsesOrderOnlyBuildNumberOrDateFile(t *testing.T) {
 testCases := []struct {
  name            string
  bp              string
  fs              android.MockFS
  expectedError   string
  expectedCommand string
 }{
  {
   name: "buld number not allowed when not in allowlist",
   fs: android.MockFS{
    "foo/Android.bp": []byte(`
genrule {
 name: "gen",
 uses_order_only_build_number_file: true,
 cmd: "cp $(build_number_file) $(out)",
 out: ["out.txt"],
}
`),
   },
   expectedError: `Only allowlisted modules may use uses_order_only_build_number_file: true`,
  },
  {
   name: "build number normal",
   fs: android.MockFS{
    "build/soong/tests/Android.bp": []byte(`
genrule {
 name: "gen",
 uses_order_only_build_number_file: true,
 cmd: "cp $(build_number_file) $(out)",
 out: ["out.txt"],
}
`),
   },
   expectedCommand: `cp __SBOX_SANDBOX_DIR__/out/soong/build_number.txt __SBOX_SANDBOX_DIR__/out/out.txt`,
  },
  {
   name: "build date not allowed when not in allowlist",
   fs: android.MockFS{
    "foo/Android.bp": []byte(`
    genrule {
     name: "gen",
     uses_order_only_build_date_file: true,
     cmd: "cp $(build_date_file) $(out)",
     out: ["out.txt"],
    }
    `),
   },
   expectedError: `Only allowlisted modules may use uses_order_only_build_date_file: true`,
  },
  {
   name: "build date normal",
   fs: android.MockFS{
    "build/soong/tests/Android.bp": []byte(`
    genrule {
     name: "gen",
     uses_order_only_build_date_file: true,
     cmd: "cp $(build_date_file) $(out)",
     out: ["out.txt"],
    }
    `),
   },
   expectedCommand: `cp __SBOX_SANDBOX_DIR__/out/build_date.txt __SBOX_SANDBOX_DIR__/out/out.txt`,
  },
 }

 for _, tc := range testCases {
  t.Run(tc.name, func(t *testing.T) {
   fixtures := android.GroupFixturePreparers(
    prepareForGenRuleTest,
    android.PrepareForTestWithVisibility,
    android.FixtureMergeMockFs(tc.fs),
    android.FixtureModifyConfigAndContext(func(config android.Config, ctx *android.TestContext) {
     config.TestProductVariables.BuildNumberFile = proptools.StringPtr("build_number.txt")
    }),
    android.SetBuildDateFileEnvVarForTests(),
   )
   if tc.expectedError != "" {
    fixtures = fixtures.ExtendWithErrorHandler(android.FixtureExpectsOneErrorPattern(tc.expectedError))
   }
   result := fixtures.RunTest(t)

   if tc.expectedError == "" {
    gen := result.Module("gen", "").(*Module)
    android.AssertStringEquals(t, "raw commands", tc.expectedCommand, gen.rawCommands[0])
   }
  })
 }
}

func TestGensrcsOutputProperty(t *testing.T) {
 testCases := []struct {
  name          string
  bp            string
  fs            android.MockFS
  expectedError string
  expectedOuts  []string
 }{
  {
   name: "using in:base to add prefix and change extension",
   fs: android.MockFS{
    "foo/Android.bp": []byte(`
gensrcs {
 name: "gen",
 srcs: ["a.txt", "b.txt"],
 cmd: "cp $(in) $(out)",
 output: "prefix_$(in:base).c",
}
`),
   },
   expectedOuts: []string{
    "out/soong/.intermediates/foo/gen/gen/gensrcs/prefix_a.c",
    "out/soong/.intermediates/foo/gen/gen/gensrcs/prefix_b.c",
   },
  },
  {
   name: "Full paths with $(in)",
   fs: android.MockFS{
    "foo/Android.bp": []byte(`
gensrcs {
 name: "gen",
 srcs: ["bar/a.txt", "baz/b.txt"],
 cmd: "cp $(in) $(out)",
 output: "$(in).c",
}
`),
   },
   expectedOuts: []string{
    "out/soong/.intermediates/foo/gen/gen/gensrcs/foo/bar/a.txt.c",
    "out/soong/.intermediates/foo/gen/gen/gensrcs/foo/baz/b.txt.c",
   },
  },
  {
   name: "Using $(in:fullpath/base)",
   fs: android.MockFS{
    "foo/Android.bp": []byte(`
gensrcs {
 name: "gen",
 srcs: ["bar/a.txt", "baz/b.txt"],
 cmd: "cp $(in) $(out)",
 output: "$(in:fullpath/base).c",
}
`),
   },
   expectedOuts: []string{
    "out/soong/.intermediates/foo/gen/gen/gensrcs/foo/bar/a.c",
    "out/soong/.intermediates/foo/gen/gen/gensrcs/foo/baz/b.c",
   },
  },
  {
   name: "Using $(in:relpath/base)",
   fs: android.MockFS{
    "foo/Android.bp": []byte(`
gensrcs {
 name: "gen",
 srcs: ["bar/a.txt", "baz/b.txt"],
 cmd: "cp $(in) $(out)",
 output: "$(in:relpath/base).c",
}
`),
   },
   expectedOuts: []string{
    "out/soong/.intermediates/foo/gen/gen/gensrcs/bar/a.c",
    "out/soong/.intermediates/foo/gen/gen/gensrcs/baz/b.c",
   },
  },
  {
   name: "Unknown variable",
   fs: android.MockFS{
    "foo/Android.bp": []byte(`
gensrcs {
 name: "gen",
 srcs: ["bar/a.txt", "baz/b.txt"],
 cmd: "cp $(in) $(out)",
 output: "$(in:foo).c",
}
`),
   },
   expectedError: `unknown variable '\$\(in:foo\)'`,
  },
  {
   name: "Cannot specify both output and output_extension",
   fs: android.MockFS{
    "foo/Android.bp": []byte(`
gensrcs {
 name: "gen",
 srcs: ["bar/a.txt", "baz/b.txt"],
 cmd: "cp $(in) $(out)",
 output: "$(in:base).c",
 output_extension: "c",
}
`),
   },
   expectedError: `cannot specify both output and output_extension`,
  },
 }

 for _, tc := range testCases {
  t.Run(tc.name, func(t *testing.T) {
   fixtures := android.GroupFixturePreparers(
    prepareForGenRuleTest,
    android.PrepareForTestWithVisibility,
    android.FixtureMergeMockFs(tc.fs),
    android.FixtureModifyConfigAndContext(func(config android.Config, ctx *android.TestContext) {
     config.TestProductVariables.BuildNumberFile = proptools.StringPtr("build_number.txt")
    }),
    android.SetBuildDateFileEnvVarForTests(),
   )
   if tc.expectedError != "" {
    fixtures = fixtures.ExtendWithErrorHandler(android.FixtureExpectsOneErrorPattern(tc.expectedError))
   }
   result := fixtures.RunTest(t)

   if tc.expectedError == "" {
    gen := result.Module("gen", "").(*Module)
    android.AssertPathsRelativeToTopEquals(t, "files", tc.expectedOuts, gen.outputFiles)
   }
  })
 }
}

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) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 t.outputFile = ctx.InstallFile(android.PathForModuleInstall(ctx, "bin"), ctx.ModuleName(), android.PathForOutput(ctx, ctx.ModuleName()))
}

func (t *testTool) HostToolPath() android.OptionalPath {
 return android.OptionalPathForPath(t.outputFile)
}

type prebuiltTestTool struct {
 android.ModuleBase
 prebuilt android.Prebuilt
 testTool
}

func (p *prebuiltTestTool) Name() string {
 return p.prebuilt.Name(p.ModuleBase.Name())
}

func (p *prebuiltTestTool) Prebuilt() *android.Prebuilt {
 return &p.prebuilt
}

func (t *prebuiltTestTool) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 t.outputFile = ctx.InstallFile(android.PathForModuleInstall(ctx, "prebuilt_bin"), ctx.ModuleName(), android.PathForOutput(ctx, ctx.ModuleName()))
}

func prebuiltToolFactory() android.Module {
 module := &prebuiltTestTool{}
 android.InitPrebuiltModuleWithoutSrcs(module)
 android.InitAndroidArchModule(module, android.HostSupported, android.MultilibFirst)
 return module
}

var _ android.HostToolProvider = (*testTool)(nil)
var _ android.HostToolProvider = (*prebuiltTestTool)(nil)

type testOutputProducer struct {
 android.ModuleBase
 outputFile android.Path
}

func outputProducerFactory() android.Module {
 module := &testOutputProducer{}
 android.InitAndroidArchModule(module, android.HostSupported, android.MultilibFirst)
 return module
}

func (t *testOutputProducer) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 t.outputFile = ctx.InstallFile(android.PathForModuleInstall(ctx, "bin"), ctx.ModuleName(), android.PathForOutput(ctx, ctx.ModuleName()))
}

type useSource struct {
 android.ModuleBase
 props struct {
  Srcs []string `android:"path"`
 }
 srcs android.Paths
}

func (s *useSource) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 s.srcs = android.PathsForModuleSrc(ctx, s.props.Srcs)
}

func useSourceFactory() android.Module {
 module := &useSource{}
 module.AddProperties(&module.props)
 android.InitAndroidModule(module)
 return module
}

[Dauer der Verarbeitung: 0.27 Sekunden, vorverarbeitet 2026-06-28]

                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik