123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161 |
- // 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"
- "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: "depfile",
- moduleName: "depfile_allowed_for_test",
- prop: `
- out: ["out"],
- depfile: true,
- cmd: "echo foo > $(out) && touch $(depfile)",
- `,
- expect: "echo foo > __SBOX_SANDBOX_DIR__/out/out && touch __SBOX_DEPFILE__",
- },
- {
- 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 depfile",
- prop: `
- out: ["out"],
- cmd: "echo foo > $(out) && touch $(depfile)",
- `,
- err: "$(depfile) used without depfile property",
- },
- {
- name: "error no depfile",
- moduleName: "depfile_allowed_for_test",
- 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",
- },
- {
- 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.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
- variables.GenruleSandboxing = proptools.BoolPtr(true)
- }),
- 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(test.name, "")
- manifest := android.RuleBuilderSboxProtoForTests(t, 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
- }{
- {
- 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",
- },
- },
- }
- 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)
- if expectedErrors != nil {
- return
- }
- 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 TestGenruleAllowlistingDepfile(t *testing.T) {
- tests := []struct {
- name string
- prop string
- err string
- moduleName string
- }{
- {
- name: `error when module is not allowlisted`,
- prop: `
- depfile: true,
- cmd: "cat $(in) > $(out) && cat $(depfile)",
- `,
- err: "depfile: Deprecated to ensure the module type is convertible to Bazel",
- },
- {
- name: `no error when module is allowlisted`,
- prop: `
- depfile: true,
- cmd: "cat $(in) > $(out) && cat $(depfile)",
- `,
- moduleName: `depfile_allowed_for_test`,
- },
- }
- for _, test := range tests {
- t.Run(test.name, func(t *testing.T) {
- moduleName := "foo"
- if test.moduleName != "" {
- moduleName = test.moduleName
- }
- bp := fmt.Sprintf(`
- gensrcs {
- name: "%s",
- srcs: ["data.txt"],
- %s
- }`, moduleName, test.prop)
- var expectedErrors []string
- if test.err != "" {
- expectedErrors = append(expectedErrors, test.err)
- }
- android.GroupFixturePreparers(
- prepareForGenRuleTest,
- android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
- variables.GenruleSandboxing = proptools.BoolPtr(true)
- }),
- ).
- ExtendWithErrorHandler(android.FixtureExpectsAllErrorsToMatchAPattern(expectedErrors)).
- RunTestWithBp(t, bp)
- })
- }
- }
- 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])
- expectedSrcs := []string{"in1"}
- android.AssertDeepEquals(t, "srcs", expectedSrcs, gen.properties.Srcs)
- }
- 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("gen", "").Output("out")
- if gen.Rule != android.ErrorRule {
- 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("gen_foo", "").Module().(*useSource).srcs)
- android.AssertPathsRelativeToTopEquals(t,
- "genrule.tag with output in subdir",
- []string{"out/soong/.intermediates/gen/gen/sub/bar"},
- result.ModuleForTests("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("gen_all", "").Module().(*useSource).srcs)
- }
- 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 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 TestGenruleWithBazel(t *testing.T) {
- bp := `
- genrule {
- name: "foo",
- out: ["one.txt", "two.txt"],
- bazel_module: { label: "//foo/bar:bar" },
- }
- `
- result := android.GroupFixturePreparers(
- prepareForGenRuleTest, android.FixtureModifyConfig(func(config android.Config) {
- config.BazelContext = android.MockBazelContext{
- OutputBaseDir: "outputbase",
- LabelToOutputFiles: map[string][]string{
- "//foo/bar:bar": []string{"bazelone.txt", "bazeltwo.txt"}}}
- })).RunTestWithBp(t, testGenruleBp()+bp)
- gen := result.Module("foo", "").(*Module)
- expectedOutputFiles := []string{"outputbase/execroot/__main__/bazelone.txt",
- "outputbase/execroot/__main__/bazeltwo.txt"}
- android.AssertDeepEquals(t, "output files", expectedOutputFiles, gen.outputFiles.Strings())
- android.AssertDeepEquals(t, "output deps", expectedOutputFiles, gen.outputDeps.Strings())
- }
- 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])
- })
- }
- }
- 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()))
- }
- func (t *testOutputProducer) OutputFiles(tag string) (android.Paths, error) {
- return android.Paths{t.outputFile}, nil
- }
- var _ android.OutputFileProducer = (*testOutputProducer)(nil)
- 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
- }
|