platform_build_soong/bp2build/build_conversion_test.go
Jingwen Chen 4910976314 Make BUILD file merging slightly smarter.
This change enables checked-in BUILD files like
prebuilts/clang/host/linux-x86/BUILD.bazel to be merged cleanly with the
bp2build generated one into the synthetic workspace.

The checked-in BUILD file contains a package() declaration that bp2build
also generates. To avoid double declaration, the BUILD file writer now
checks if the BazelTargets contain handcrafted targets. If so, it
delegates the package declaration to the handcrafted BUILD file instead.

This change also sorts the bp2build targets before the handcrafted ones,
and adds a section header to demarcate the two sets of targets.

Test: TH
Change-Id: I3ecdeaab3226b895b623daf0791d24a657f7a7c6
2021-05-25 07:01:55 +00:00

1634 lines
46 KiB
Go

// Copyright 2020 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 bp2build
import (
"android/soong/android"
"android/soong/genrule"
"strings"
"testing"
)
func TestGenerateSoongModuleTargets(t *testing.T) {
testCases := []struct {
bp string
expectedBazelTarget string
}{
{
bp: `custom { name: "foo" }
`,
expectedBazelTarget: `soong_module(
name = "foo",
soong_module_name = "foo",
soong_module_type = "custom",
soong_module_variant = "",
soong_module_deps = [
],
)`,
},
{
bp: `custom {
name: "foo",
ramdisk: true,
}
`,
expectedBazelTarget: `soong_module(
name = "foo",
soong_module_name = "foo",
soong_module_type = "custom",
soong_module_variant = "",
soong_module_deps = [
],
ramdisk = True,
)`,
},
{
bp: `custom {
name: "foo",
owner: "a_string_with\"quotes\"_and_\\backslashes\\\\",
}
`,
expectedBazelTarget: `soong_module(
name = "foo",
soong_module_name = "foo",
soong_module_type = "custom",
soong_module_variant = "",
soong_module_deps = [
],
owner = "a_string_with\"quotes\"_and_\\backslashes\\\\",
)`,
},
{
bp: `custom {
name: "foo",
required: ["bar"],
}
`,
expectedBazelTarget: `soong_module(
name = "foo",
soong_module_name = "foo",
soong_module_type = "custom",
soong_module_variant = "",
soong_module_deps = [
],
required = ["bar"],
)`,
},
{
bp: `custom {
name: "foo",
target_required: ["qux", "bazqux"],
}
`,
expectedBazelTarget: `soong_module(
name = "foo",
soong_module_name = "foo",
soong_module_type = "custom",
soong_module_variant = "",
soong_module_deps = [
],
target_required = [
"qux",
"bazqux",
],
)`,
},
{
bp: `custom {
name: "foo",
dist: {
targets: ["goal_foo"],
tag: ".foo",
},
dists: [{
targets: ["goal_bar"],
tag: ".bar",
}],
}
`,
expectedBazelTarget: `soong_module(
name = "foo",
soong_module_name = "foo",
soong_module_type = "custom",
soong_module_variant = "",
soong_module_deps = [
],
dist = {
"tag": ".foo",
"targets": ["goal_foo"],
},
dists = [{
"tag": ".bar",
"targets": ["goal_bar"],
}],
)`,
},
{
bp: `custom {
name: "foo",
required: ["bar"],
target_required: ["qux", "bazqux"],
ramdisk: true,
owner: "custom_owner",
dists: [
{
tag: ".tag",
targets: ["my_goal"],
},
],
}
`,
expectedBazelTarget: `soong_module(
name = "foo",
soong_module_name = "foo",
soong_module_type = "custom",
soong_module_variant = "",
soong_module_deps = [
],
dists = [{
"tag": ".tag",
"targets": ["my_goal"],
}],
owner = "custom_owner",
ramdisk = True,
required = ["bar"],
target_required = [
"qux",
"bazqux",
],
)`,
},
}
dir := "."
for _, testCase := range testCases {
config := android.TestConfig(buildDir, nil, testCase.bp, nil)
ctx := android.NewTestContext(config)
ctx.RegisterModuleType("custom", customModuleFactory)
ctx.Register()
_, errs := ctx.ParseFileList(dir, []string{"Android.bp"})
android.FailIfErrored(t, errs)
_, errs = ctx.PrepareBuildActions(config)
android.FailIfErrored(t, errs)
codegenCtx := NewCodegenContext(config, *ctx.Context, QueryView)
bazelTargets := generateBazelTargetsForDir(codegenCtx, dir)
if actualCount, expectedCount := len(bazelTargets), 1; actualCount != expectedCount {
t.Fatalf("Expected %d bazel target, got %d", expectedCount, actualCount)
}
actualBazelTarget := bazelTargets[0]
if actualBazelTarget.content != testCase.expectedBazelTarget {
t.Errorf(
"Expected generated Bazel target to be '%s', got '%s'",
testCase.expectedBazelTarget,
actualBazelTarget.content,
)
}
}
}
func TestGenerateBazelTargetModules(t *testing.T) {
testCases := []struct {
name string
bp string
expectedBazelTargets []string
}{
{
bp: `custom {
name: "foo",
string_list_prop: ["a", "b"],
string_prop: "a",
bazel_module: { bp2build_available: true },
}`,
expectedBazelTargets: []string{`custom(
name = "foo",
string_list_prop = [
"a",
"b",
],
string_prop = "a",
)`,
},
},
{
bp: `custom {
name: "control_characters",
string_list_prop: ["\t", "\n"],
string_prop: "a\t\n\r",
bazel_module: { bp2build_available: true },
}`,
expectedBazelTargets: []string{`custom(
name = "control_characters",
string_list_prop = [
"\t",
"\n",
],
string_prop = "a\t\n\r",
)`,
},
},
{
bp: `custom {
name: "has_dep",
arch_paths: [":dep"],
bazel_module: { bp2build_available: true },
}
custom {
name: "dep",
arch_paths: ["abc"],
bazel_module: { bp2build_available: true },
}`,
expectedBazelTargets: []string{`custom(
name = "dep",
arch_paths = ["abc"],
)`,
`custom(
name = "has_dep",
arch_paths = [":dep"],
)`,
},
},
{
bp: `custom {
name: "arch_paths",
arch: {
x86: {
arch_paths: ["abc"],
},
},
bazel_module: { bp2build_available: true },
}`,
expectedBazelTargets: []string{`custom(
name = "arch_paths",
arch_paths = select({
"//build/bazel/platforms/arch:x86": ["abc"],
"//conditions:default": [],
}),
)`,
},
},
{
bp: `custom {
name: "has_dep",
arch: {
x86: {
arch_paths: [":dep"],
},
},
bazel_module: { bp2build_available: true },
}
custom {
name: "dep",
arch_paths: ["abc"],
bazel_module: { bp2build_available: true },
}`,
expectedBazelTargets: []string{`custom(
name = "dep",
arch_paths = ["abc"],
)`,
`custom(
name = "has_dep",
arch_paths = select({
"//build/bazel/platforms/arch:x86": [":dep"],
"//conditions:default": [],
}),
)`,
},
},
}
dir := "."
for _, testCase := range testCases {
config := android.TestConfig(buildDir, nil, testCase.bp, nil)
ctx := android.NewTestContext(config)
ctx.RegisterModuleType("custom", customModuleFactory)
ctx.RegisterBp2BuildMutator("custom", customBp2BuildMutator)
ctx.RegisterForBazelConversion()
_, errs := ctx.ParseFileList(dir, []string{"Android.bp"})
if errored(t, "", errs) {
continue
}
_, errs = ctx.ResolveDependencies(config)
if errored(t, "", errs) {
continue
}
codegenCtx := NewCodegenContext(config, *ctx.Context, Bp2Build)
bazelTargets := generateBazelTargetsForDir(codegenCtx, dir)
if actualCount, expectedCount := len(bazelTargets), len(testCase.expectedBazelTargets); actualCount != expectedCount {
t.Errorf("Expected %d bazel target, got %d", expectedCount, actualCount)
} else {
for i, expectedBazelTarget := range testCase.expectedBazelTargets {
actualBazelTarget := bazelTargets[i]
if actualBazelTarget.content != expectedBazelTarget {
t.Errorf(
"Expected generated Bazel target to be '%s', got '%s'",
expectedBazelTarget,
actualBazelTarget.content,
)
}
}
}
}
}
func TestLoadStatements(t *testing.T) {
testCases := []struct {
bazelTargets BazelTargets
expectedLoadStatements string
}{
{
bazelTargets: BazelTargets{
BazelTarget{
name: "foo",
ruleClass: "cc_library",
bzlLoadLocation: "//build/bazel/rules:cc.bzl",
},
},
expectedLoadStatements: `load("//build/bazel/rules:cc.bzl", "cc_library")`,
},
{
bazelTargets: BazelTargets{
BazelTarget{
name: "foo",
ruleClass: "cc_library",
bzlLoadLocation: "//build/bazel/rules:cc.bzl",
},
BazelTarget{
name: "bar",
ruleClass: "cc_library",
bzlLoadLocation: "//build/bazel/rules:cc.bzl",
},
},
expectedLoadStatements: `load("//build/bazel/rules:cc.bzl", "cc_library")`,
},
{
bazelTargets: BazelTargets{
BazelTarget{
name: "foo",
ruleClass: "cc_library",
bzlLoadLocation: "//build/bazel/rules:cc.bzl",
},
BazelTarget{
name: "bar",
ruleClass: "cc_binary",
bzlLoadLocation: "//build/bazel/rules:cc.bzl",
},
},
expectedLoadStatements: `load("//build/bazel/rules:cc.bzl", "cc_binary", "cc_library")`,
},
{
bazelTargets: BazelTargets{
BazelTarget{
name: "foo",
ruleClass: "cc_library",
bzlLoadLocation: "//build/bazel/rules:cc.bzl",
},
BazelTarget{
name: "bar",
ruleClass: "cc_binary",
bzlLoadLocation: "//build/bazel/rules:cc.bzl",
},
BazelTarget{
name: "baz",
ruleClass: "java_binary",
bzlLoadLocation: "//build/bazel/rules:java.bzl",
},
},
expectedLoadStatements: `load("//build/bazel/rules:cc.bzl", "cc_binary", "cc_library")
load("//build/bazel/rules:java.bzl", "java_binary")`,
},
{
bazelTargets: BazelTargets{
BazelTarget{
name: "foo",
ruleClass: "cc_binary",
bzlLoadLocation: "//build/bazel/rules:cc.bzl",
},
BazelTarget{
name: "bar",
ruleClass: "java_binary",
bzlLoadLocation: "//build/bazel/rules:java.bzl",
},
BazelTarget{
name: "baz",
ruleClass: "genrule",
// Note: no bzlLoadLocation for native rules
},
},
expectedLoadStatements: `load("//build/bazel/rules:cc.bzl", "cc_binary")
load("//build/bazel/rules:java.bzl", "java_binary")`,
},
}
for _, testCase := range testCases {
actual := testCase.bazelTargets.LoadStatements()
expected := testCase.expectedLoadStatements
if actual != expected {
t.Fatalf("Expected load statements to be %s, got %s", expected, actual)
}
}
}
func TestGenerateBazelTargetModules_OneToMany_LoadedFromStarlark(t *testing.T) {
testCases := []struct {
bp string
expectedBazelTarget string
expectedBazelTargetCount int
expectedLoadStatements string
}{
{
bp: `custom {
name: "bar",
bazel_module: { bp2build_available: true },
}`,
expectedBazelTarget: `my_library(
name = "bar",
)
my_proto_library(
name = "bar_my_proto_library_deps",
)
proto_library(
name = "bar_proto_library_deps",
)`,
expectedBazelTargetCount: 3,
expectedLoadStatements: `load("//build/bazel/rules:proto.bzl", "my_proto_library", "proto_library")
load("//build/bazel/rules:rules.bzl", "my_library")`,
},
}
dir := "."
for _, testCase := range testCases {
config := android.TestConfig(buildDir, nil, testCase.bp, nil)
ctx := android.NewTestContext(config)
ctx.RegisterModuleType("custom", customModuleFactory)
ctx.RegisterBp2BuildMutator("custom", customBp2BuildMutatorFromStarlark)
ctx.RegisterForBazelConversion()
_, errs := ctx.ParseFileList(dir, []string{"Android.bp"})
android.FailIfErrored(t, errs)
_, errs = ctx.ResolveDependencies(config)
android.FailIfErrored(t, errs)
codegenCtx := NewCodegenContext(config, *ctx.Context, Bp2Build)
bazelTargets := generateBazelTargetsForDir(codegenCtx, dir)
if actualCount := len(bazelTargets); actualCount != testCase.expectedBazelTargetCount {
t.Fatalf("Expected %d bazel target, got %d", testCase.expectedBazelTargetCount, actualCount)
}
actualBazelTargets := bazelTargets.String()
if actualBazelTargets != testCase.expectedBazelTarget {
t.Errorf(
"Expected generated Bazel target to be '%s', got '%s'",
testCase.expectedBazelTarget,
actualBazelTargets,
)
}
actualLoadStatements := bazelTargets.LoadStatements()
if actualLoadStatements != testCase.expectedLoadStatements {
t.Errorf(
"Expected generated load statements to be '%s', got '%s'",
testCase.expectedLoadStatements,
actualLoadStatements,
)
}
}
}
func TestModuleTypeBp2Build(t *testing.T) {
otherGenruleBp := map[string]string{
"other/Android.bp": `genrule {
name: "foo.tool",
out: ["foo_tool.out"],
srcs: ["foo_tool.in"],
cmd: "cp $(in) $(out)",
}
genrule {
name: "other.tool",
out: ["other_tool.out"],
srcs: ["other_tool.in"],
cmd: "cp $(in) $(out)",
}`,
}
testCases := []struct {
description string
moduleTypeUnderTest string
moduleTypeUnderTestFactory android.ModuleFactory
moduleTypeUnderTestBp2BuildMutator func(android.TopDownMutatorContext)
preArchMutators []android.RegisterMutatorFunc
depsMutators []android.RegisterMutatorFunc
bp string
expectedBazelTargets []string
fs map[string]string
dir string
}{
{
description: "filegroup with does not specify srcs",
moduleTypeUnderTest: "filegroup",
moduleTypeUnderTestFactory: android.FileGroupFactory,
moduleTypeUnderTestBp2BuildMutator: android.FilegroupBp2Build,
bp: `filegroup {
name: "fg_foo",
bazel_module: { bp2build_available: true },
}`,
expectedBazelTargets: []string{
`filegroup(
name = "fg_foo",
)`,
},
},
{
description: "filegroup with no srcs",
moduleTypeUnderTest: "filegroup",
moduleTypeUnderTestFactory: android.FileGroupFactory,
moduleTypeUnderTestBp2BuildMutator: android.FilegroupBp2Build,
bp: `filegroup {
name: "fg_foo",
srcs: [],
bazel_module: { bp2build_available: true },
}`,
expectedBazelTargets: []string{
`filegroup(
name = "fg_foo",
)`,
},
},
{
description: "filegroup with srcs",
moduleTypeUnderTest: "filegroup",
moduleTypeUnderTestFactory: android.FileGroupFactory,
moduleTypeUnderTestBp2BuildMutator: android.FilegroupBp2Build,
bp: `filegroup {
name: "fg_foo",
srcs: ["a", "b"],
bazel_module: { bp2build_available: true },
}`,
expectedBazelTargets: []string{`filegroup(
name = "fg_foo",
srcs = [
"a",
"b",
],
)`,
},
},
{
description: "filegroup with excludes srcs",
moduleTypeUnderTest: "filegroup",
moduleTypeUnderTestFactory: android.FileGroupFactory,
moduleTypeUnderTestBp2BuildMutator: android.FilegroupBp2Build,
bp: `filegroup {
name: "fg_foo",
srcs: ["a", "b"],
exclude_srcs: ["a"],
bazel_module: { bp2build_available: true },
}`,
expectedBazelTargets: []string{`filegroup(
name = "fg_foo",
srcs = ["b"],
)`,
},
},
{
description: "filegroup with glob",
moduleTypeUnderTest: "filegroup",
moduleTypeUnderTestFactory: android.FileGroupFactory,
moduleTypeUnderTestBp2BuildMutator: android.FilegroupBp2Build,
bp: `filegroup {
name: "foo",
srcs: ["**/*.txt"],
bazel_module: { bp2build_available: true },
}`,
expectedBazelTargets: []string{`filegroup(
name = "foo",
srcs = [
"other/a.txt",
"other/b.txt",
"other/subdir/a.txt",
],
)`,
},
fs: map[string]string{
"other/a.txt": "",
"other/b.txt": "",
"other/subdir/a.txt": "",
"other/file": "",
},
},
{
description: "filegroup with glob in subdir",
moduleTypeUnderTest: "filegroup",
moduleTypeUnderTestFactory: android.FileGroupFactory,
moduleTypeUnderTestBp2BuildMutator: android.FilegroupBp2Build,
bp: `filegroup {
name: "foo",
srcs: ["a.txt"],
bazel_module: { bp2build_available: true },
}`,
dir: "other",
expectedBazelTargets: []string{`filegroup(
name = "fg_foo",
srcs = [
"a.txt",
"b.txt",
"subdir/a.txt",
],
)`,
},
fs: map[string]string{
"other/Android.bp": `filegroup {
name: "fg_foo",
srcs: ["**/*.txt"],
bazel_module: { bp2build_available: true },
}`,
"other/a.txt": "",
"other/b.txt": "",
"other/subdir/a.txt": "",
"other/file": "",
},
},
{
description: "depends_on_other_dir_module",
moduleTypeUnderTest: "filegroup",
moduleTypeUnderTestFactory: android.FileGroupFactory,
moduleTypeUnderTestBp2BuildMutator: android.FilegroupBp2Build,
bp: `filegroup {
name: "foobar",
srcs: [
":foo",
"c",
],
bazel_module: { bp2build_available: true },
}`,
expectedBazelTargets: []string{`filegroup(
name = "foobar",
srcs = [
"//other:foo",
"c",
],
)`,
},
fs: map[string]string{
"other/Android.bp": `filegroup {
name: "foo",
srcs: ["a", "b"],
}`,
},
},
{
description: "genrule with command line variable replacements",
moduleTypeUnderTest: "genrule",
moduleTypeUnderTestFactory: genrule.GenRuleFactory,
moduleTypeUnderTestBp2BuildMutator: genrule.GenruleBp2Build,
depsMutators: []android.RegisterMutatorFunc{genrule.RegisterGenruleBp2BuildDeps},
bp: `genrule {
name: "foo.tool",
out: ["foo_tool.out"],
srcs: ["foo_tool.in"],
cmd: "cp $(in) $(out)",
bazel_module: { bp2build_available: true },
}
genrule {
name: "foo",
out: ["foo.out"],
srcs: ["foo.in"],
tools: [":foo.tool"],
cmd: "$(location :foo.tool) --genDir=$(genDir) arg $(in) $(out)",
bazel_module: { bp2build_available: true },
}`,
expectedBazelTargets: []string{
`genrule(
name = "foo",
cmd = "$(location :foo.tool) --genDir=$(GENDIR) arg $(SRCS) $(OUTS)",
outs = ["foo.out"],
srcs = ["foo.in"],
tools = [":foo.tool"],
)`,
`genrule(
name = "foo.tool",
cmd = "cp $(SRCS) $(OUTS)",
outs = ["foo_tool.out"],
srcs = ["foo_tool.in"],
)`,
},
},
{
description: "genrule using $(locations :label)",
moduleTypeUnderTest: "genrule",
moduleTypeUnderTestFactory: genrule.GenRuleFactory,
moduleTypeUnderTestBp2BuildMutator: genrule.GenruleBp2Build,
depsMutators: []android.RegisterMutatorFunc{genrule.RegisterGenruleBp2BuildDeps},
bp: `genrule {
name: "foo.tools",
out: ["foo_tool.out", "foo_tool2.out"],
srcs: ["foo_tool.in"],
cmd: "cp $(in) $(out)",
bazel_module: { bp2build_available: true },
}
genrule {
name: "foo",
out: ["foo.out"],
srcs: ["foo.in"],
tools: [":foo.tools"],
cmd: "$(locations :foo.tools) -s $(out) $(in)",
bazel_module: { bp2build_available: true },
}`,
expectedBazelTargets: []string{`genrule(
name = "foo",
cmd = "$(locations :foo.tools) -s $(OUTS) $(SRCS)",
outs = ["foo.out"],
srcs = ["foo.in"],
tools = [":foo.tools"],
)`,
`genrule(
name = "foo.tools",
cmd = "cp $(SRCS) $(OUTS)",
outs = [
"foo_tool.out",
"foo_tool2.out",
],
srcs = ["foo_tool.in"],
)`,
},
},
{
description: "genrule using $(locations //absolute:label)",
moduleTypeUnderTest: "genrule",
moduleTypeUnderTestFactory: genrule.GenRuleFactory,
moduleTypeUnderTestBp2BuildMutator: genrule.GenruleBp2Build,
depsMutators: []android.RegisterMutatorFunc{genrule.RegisterGenruleBp2BuildDeps},
bp: `genrule {
name: "foo",
out: ["foo.out"],
srcs: ["foo.in"],
tool_files: [":foo.tool"],
cmd: "$(locations :foo.tool) -s $(out) $(in)",
bazel_module: { bp2build_available: true },
}`,
expectedBazelTargets: []string{`genrule(
name = "foo",
cmd = "$(locations //other:foo.tool) -s $(OUTS) $(SRCS)",
outs = ["foo.out"],
srcs = ["foo.in"],
tools = ["//other:foo.tool"],
)`,
},
fs: otherGenruleBp,
},
{
description: "genrule srcs using $(locations //absolute:label)",
moduleTypeUnderTest: "genrule",
moduleTypeUnderTestFactory: genrule.GenRuleFactory,
moduleTypeUnderTestBp2BuildMutator: genrule.GenruleBp2Build,
depsMutators: []android.RegisterMutatorFunc{genrule.RegisterGenruleBp2BuildDeps},
bp: `genrule {
name: "foo",
out: ["foo.out"],
srcs: [":other.tool"],
tool_files: [":foo.tool"],
cmd: "$(locations :foo.tool) -s $(out) $(location :other.tool)",
bazel_module: { bp2build_available: true },
}`,
expectedBazelTargets: []string{`genrule(
name = "foo",
cmd = "$(locations //other:foo.tool) -s $(OUTS) $(location //other:other.tool)",
outs = ["foo.out"],
srcs = ["//other:other.tool"],
tools = ["//other:foo.tool"],
)`,
},
fs: otherGenruleBp,
},
{
description: "genrule using $(location) label should substitute first tool label automatically",
moduleTypeUnderTest: "genrule",
moduleTypeUnderTestFactory: genrule.GenRuleFactory,
moduleTypeUnderTestBp2BuildMutator: genrule.GenruleBp2Build,
depsMutators: []android.RegisterMutatorFunc{genrule.RegisterGenruleBp2BuildDeps},
bp: `genrule {
name: "foo",
out: ["foo.out"],
srcs: ["foo.in"],
tool_files: [":foo.tool", ":other.tool"],
cmd: "$(location) -s $(out) $(in)",
bazel_module: { bp2build_available: true },
}`,
expectedBazelTargets: []string{`genrule(
name = "foo",
cmd = "$(location //other:foo.tool) -s $(OUTS) $(SRCS)",
outs = ["foo.out"],
srcs = ["foo.in"],
tools = [
"//other:foo.tool",
"//other:other.tool",
],
)`,
},
fs: otherGenruleBp,
},
{
description: "genrule using $(locations) label should substitute first tool label automatically",
moduleTypeUnderTest: "genrule",
moduleTypeUnderTestFactory: genrule.GenRuleFactory,
moduleTypeUnderTestBp2BuildMutator: genrule.GenruleBp2Build,
depsMutators: []android.RegisterMutatorFunc{genrule.RegisterGenruleBp2BuildDeps},
bp: `genrule {
name: "foo",
out: ["foo.out"],
srcs: ["foo.in"],
tools: [":foo.tool", ":other.tool"],
cmd: "$(locations) -s $(out) $(in)",
bazel_module: { bp2build_available: true },
}`,
expectedBazelTargets: []string{`genrule(
name = "foo",
cmd = "$(locations //other:foo.tool) -s $(OUTS) $(SRCS)",
outs = ["foo.out"],
srcs = ["foo.in"],
tools = [
"//other:foo.tool",
"//other:other.tool",
],
)`,
},
fs: otherGenruleBp,
},
{
description: "genrule without tools or tool_files can convert successfully",
moduleTypeUnderTest: "genrule",
moduleTypeUnderTestFactory: genrule.GenRuleFactory,
moduleTypeUnderTestBp2BuildMutator: genrule.GenruleBp2Build,
depsMutators: []android.RegisterMutatorFunc{genrule.RegisterGenruleBp2BuildDeps},
bp: `genrule {
name: "foo",
out: ["foo.out"],
srcs: ["foo.in"],
cmd: "cp $(in) $(out)",
bazel_module: { bp2build_available: true },
}`,
expectedBazelTargets: []string{`genrule(
name = "foo",
cmd = "cp $(SRCS) $(OUTS)",
outs = ["foo.out"],
srcs = ["foo.in"],
)`,
},
},
}
dir := "."
for _, testCase := range testCases {
fs := make(map[string][]byte)
toParse := []string{
"Android.bp",
}
for f, content := range testCase.fs {
if strings.HasSuffix(f, "Android.bp") {
toParse = append(toParse, f)
}
fs[f] = []byte(content)
}
config := android.TestConfig(buildDir, nil, testCase.bp, fs)
ctx := android.NewTestContext(config)
ctx.RegisterModuleType(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestFactory)
for _, m := range testCase.depsMutators {
ctx.DepsBp2BuildMutators(m)
}
ctx.RegisterBp2BuildMutator(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestBp2BuildMutator)
ctx.RegisterForBazelConversion()
_, errs := ctx.ParseFileList(dir, toParse)
if errored(t, testCase.description, errs) {
continue
}
_, errs = ctx.ResolveDependencies(config)
if errored(t, testCase.description, errs) {
continue
}
checkDir := dir
if testCase.dir != "" {
checkDir = testCase.dir
}
codegenCtx := NewCodegenContext(config, *ctx.Context, Bp2Build)
bazelTargets := generateBazelTargetsForDir(codegenCtx, checkDir)
if actualCount, expectedCount := len(bazelTargets), len(testCase.expectedBazelTargets); actualCount != expectedCount {
t.Errorf("%s: Expected %d bazel target, got %d", testCase.description, expectedCount, actualCount)
} else {
for i, target := range bazelTargets {
if w, g := testCase.expectedBazelTargets[i], target.content; w != g {
t.Errorf(
"%s: Expected generated Bazel target to be '%s', got '%s'",
testCase.description,
w,
g,
)
}
}
}
}
}
type bp2buildMutator = func(android.TopDownMutatorContext)
func TestBp2BuildInlinesDefaults(t *testing.T) {
testCases := []struct {
moduleTypesUnderTest map[string]android.ModuleFactory
bp2buildMutatorsUnderTest map[string]bp2buildMutator
bp string
expectedBazelTarget string
description string
}{
{
moduleTypesUnderTest: map[string]android.ModuleFactory{
"genrule": genrule.GenRuleFactory,
"genrule_defaults": func() android.Module { return genrule.DefaultsFactory() },
},
bp2buildMutatorsUnderTest: map[string]bp2buildMutator{
"genrule": genrule.GenruleBp2Build,
},
bp: `genrule_defaults {
name: "gen_defaults",
cmd: "do-something $(in) $(out)",
}
genrule {
name: "gen",
out: ["out"],
srcs: ["in1"],
defaults: ["gen_defaults"],
bazel_module: { bp2build_available: true },
}
`,
expectedBazelTarget: `genrule(
name = "gen",
cmd = "do-something $(SRCS) $(OUTS)",
outs = ["out"],
srcs = ["in1"],
)`,
description: "genrule applies properties from a genrule_defaults dependency if not specified",
},
{
moduleTypesUnderTest: map[string]android.ModuleFactory{
"genrule": genrule.GenRuleFactory,
"genrule_defaults": func() android.Module { return genrule.DefaultsFactory() },
},
bp2buildMutatorsUnderTest: map[string]bp2buildMutator{
"genrule": genrule.GenruleBp2Build,
},
bp: `genrule_defaults {
name: "gen_defaults",
out: ["out-from-defaults"],
srcs: ["in-from-defaults"],
cmd: "cmd-from-defaults",
}
genrule {
name: "gen",
out: ["out"],
srcs: ["in1"],
defaults: ["gen_defaults"],
cmd: "do-something $(in) $(out)",
bazel_module: { bp2build_available: true },
}
`,
expectedBazelTarget: `genrule(
name = "gen",
cmd = "do-something $(SRCS) $(OUTS)",
outs = [
"out-from-defaults",
"out",
],
srcs = [
"in-from-defaults",
"in1",
],
)`,
description: "genrule does merges properties from a genrule_defaults dependency, latest-first",
},
{
moduleTypesUnderTest: map[string]android.ModuleFactory{
"genrule": genrule.GenRuleFactory,
"genrule_defaults": func() android.Module { return genrule.DefaultsFactory() },
},
bp2buildMutatorsUnderTest: map[string]bp2buildMutator{
"genrule": genrule.GenruleBp2Build,
},
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"],
bazel_module: { bp2build_available: true },
}
`,
expectedBazelTarget: `genrule(
name = "gen",
cmd = "cp $(SRCS) $(OUTS)",
outs = ["out"],
srcs = ["in1"],
)`,
description: "genrule applies properties from list of genrule_defaults",
},
{
moduleTypesUnderTest: map[string]android.ModuleFactory{
"genrule": genrule.GenRuleFactory,
"genrule_defaults": func() android.Module { return genrule.DefaultsFactory() },
},
bp2buildMutatorsUnderTest: map[string]bp2buildMutator{
"genrule": genrule.GenruleBp2Build,
},
bp: `genrule_defaults {
name: "gen_defaults1",
defaults: ["gen_defaults2"],
cmd: "cmd1 $(in) $(out)", // overrides gen_defaults2's cmd property value.
}
genrule_defaults {
name: "gen_defaults2",
defaults: ["gen_defaults3"],
cmd: "cmd2 $(in) $(out)",
out: ["out-from-2"],
srcs: ["in1"],
}
genrule_defaults {
name: "gen_defaults3",
out: ["out-from-3"],
srcs: ["srcs-from-3"],
}
genrule {
name: "gen",
out: ["out"],
defaults: ["gen_defaults1"],
bazel_module: { bp2build_available: true },
}
`,
expectedBazelTarget: `genrule(
name = "gen",
cmd = "cmd1 $(SRCS) $(OUTS)",
outs = [
"out-from-3",
"out-from-2",
"out",
],
srcs = [
"in1",
"srcs-from-3",
],
)`,
description: "genrule applies properties from genrule_defaults transitively",
},
}
dir := "."
for _, testCase := range testCases {
config := android.TestConfig(buildDir, nil, testCase.bp, nil)
ctx := android.NewTestContext(config)
for m, factory := range testCase.moduleTypesUnderTest {
ctx.RegisterModuleType(m, factory)
}
for mutator, f := range testCase.bp2buildMutatorsUnderTest {
ctx.RegisterBp2BuildMutator(mutator, f)
}
ctx.RegisterForBazelConversion()
_, errs := ctx.ParseFileList(dir, []string{"Android.bp"})
android.FailIfErrored(t, errs)
_, errs = ctx.ResolveDependencies(config)
android.FailIfErrored(t, errs)
codegenCtx := NewCodegenContext(config, *ctx.Context, Bp2Build)
bazelTargets := generateBazelTargetsForDir(codegenCtx, dir)
if actualCount := len(bazelTargets); actualCount != 1 {
t.Fatalf("%s: Expected 1 bazel target, got %d", testCase.description, actualCount)
}
actualBazelTarget := bazelTargets[0]
if actualBazelTarget.content != testCase.expectedBazelTarget {
t.Errorf(
"%s: Expected generated Bazel target to be '%s', got '%s'",
testCase.description,
testCase.expectedBazelTarget,
actualBazelTarget.content,
)
}
}
}
func TestAllowlistingBp2buildTargetsExplicitly(t *testing.T) {
testCases := []struct {
moduleTypeUnderTest string
moduleTypeUnderTestFactory android.ModuleFactory
moduleTypeUnderTestBp2BuildMutator bp2buildMutator
bp string
expectedCount int
description string
}{
{
description: "explicitly unavailable",
moduleTypeUnderTest: "filegroup",
moduleTypeUnderTestFactory: android.FileGroupFactory,
moduleTypeUnderTestBp2BuildMutator: android.FilegroupBp2Build,
bp: `filegroup {
name: "foo",
srcs: ["a", "b"],
bazel_module: { bp2build_available: false },
}`,
expectedCount: 0,
},
{
description: "implicitly unavailable",
moduleTypeUnderTest: "filegroup",
moduleTypeUnderTestFactory: android.FileGroupFactory,
moduleTypeUnderTestBp2BuildMutator: android.FilegroupBp2Build,
bp: `filegroup {
name: "foo",
srcs: ["a", "b"],
}`,
expectedCount: 0,
},
{
description: "explicitly available",
moduleTypeUnderTest: "filegroup",
moduleTypeUnderTestFactory: android.FileGroupFactory,
moduleTypeUnderTestBp2BuildMutator: android.FilegroupBp2Build,
bp: `filegroup {
name: "foo",
srcs: ["a", "b"],
bazel_module: { bp2build_available: true },
}`,
expectedCount: 1,
},
{
description: "generates more than 1 target if needed",
moduleTypeUnderTest: "custom",
moduleTypeUnderTestFactory: customModuleFactory,
moduleTypeUnderTestBp2BuildMutator: customBp2BuildMutatorFromStarlark,
bp: `custom {
name: "foo",
bazel_module: { bp2build_available: true },
}`,
expectedCount: 3,
},
}
dir := "."
for _, testCase := range testCases {
config := android.TestConfig(buildDir, nil, testCase.bp, nil)
ctx := android.NewTestContext(config)
ctx.RegisterModuleType(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestFactory)
ctx.RegisterBp2BuildMutator(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestBp2BuildMutator)
ctx.RegisterForBazelConversion()
_, errs := ctx.ParseFileList(dir, []string{"Android.bp"})
android.FailIfErrored(t, errs)
_, errs = ctx.ResolveDependencies(config)
android.FailIfErrored(t, errs)
codegenCtx := NewCodegenContext(config, *ctx.Context, Bp2Build)
bazelTargets := generateBazelTargetsForDir(codegenCtx, dir)
if actualCount := len(bazelTargets); actualCount != testCase.expectedCount {
t.Fatalf("%s: Expected %d bazel target, got %d", testCase.description, testCase.expectedCount, actualCount)
}
}
}
func TestAllowlistingBp2buildTargetsWithConfig(t *testing.T) {
testCases := []struct {
moduleTypeUnderTest string
moduleTypeUnderTestFactory android.ModuleFactory
moduleTypeUnderTestBp2BuildMutator bp2buildMutator
expectedCount map[string]int
description string
bp2buildConfig android.Bp2BuildConfig
checkDir string
fs map[string]string
}{
{
description: "test bp2build config package and subpackages config",
moduleTypeUnderTest: "filegroup",
moduleTypeUnderTestFactory: android.FileGroupFactory,
moduleTypeUnderTestBp2BuildMutator: android.FilegroupBp2Build,
expectedCount: map[string]int{
"migrated": 1,
"migrated/but_not_really": 0,
"migrated/but_not_really/but_really": 1,
"not_migrated": 0,
"also_not_migrated": 0,
},
bp2buildConfig: android.Bp2BuildConfig{
"migrated": android.Bp2BuildDefaultTrueRecursively,
"migrated/but_not_really": android.Bp2BuildDefaultFalse,
"not_migrated": android.Bp2BuildDefaultFalse,
},
fs: map[string]string{
"migrated/Android.bp": `filegroup { name: "a" }`,
"migrated/but_not_really/Android.bp": `filegroup { name: "b" }`,
"migrated/but_not_really/but_really/Android.bp": `filegroup { name: "c" }`,
"not_migrated/Android.bp": `filegroup { name: "d" }`,
"also_not_migrated/Android.bp": `filegroup { name: "e" }`,
},
},
{
description: "test bp2build config opt-in and opt-out",
moduleTypeUnderTest: "filegroup",
moduleTypeUnderTestFactory: android.FileGroupFactory,
moduleTypeUnderTestBp2BuildMutator: android.FilegroupBp2Build,
expectedCount: map[string]int{
"package-opt-in": 2,
"package-opt-in/subpackage": 0,
"package-opt-out": 1,
"package-opt-out/subpackage": 0,
},
bp2buildConfig: android.Bp2BuildConfig{
"package-opt-in": android.Bp2BuildDefaultFalse,
"package-opt-out": android.Bp2BuildDefaultTrueRecursively,
},
fs: map[string]string{
"package-opt-in/Android.bp": `
filegroup { name: "opt-in-a" }
filegroup { name: "opt-in-b", bazel_module: { bp2build_available: true } }
filegroup { name: "opt-in-c", bazel_module: { bp2build_available: true } }
`,
"package-opt-in/subpackage/Android.bp": `
filegroup { name: "opt-in-d" } // parent package not configured to DefaultTrueRecursively
`,
"package-opt-out/Android.bp": `
filegroup { name: "opt-out-a" }
filegroup { name: "opt-out-b", bazel_module: { bp2build_available: false } }
filegroup { name: "opt-out-c", bazel_module: { bp2build_available: false } }
`,
"package-opt-out/subpackage/Android.bp": `
filegroup { name: "opt-out-g", bazel_module: { bp2build_available: false } }
filegroup { name: "opt-out-h", bazel_module: { bp2build_available: false } }
`,
},
},
}
dir := "."
for _, testCase := range testCases {
fs := make(map[string][]byte)
toParse := []string{
"Android.bp",
}
for f, content := range testCase.fs {
if strings.HasSuffix(f, "Android.bp") {
toParse = append(toParse, f)
}
fs[f] = []byte(content)
}
config := android.TestConfig(buildDir, nil, "", fs)
ctx := android.NewTestContext(config)
ctx.RegisterModuleType(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestFactory)
ctx.RegisterBp2BuildMutator(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestBp2BuildMutator)
ctx.RegisterBp2BuildConfig(testCase.bp2buildConfig)
ctx.RegisterForBazelConversion()
_, errs := ctx.ParseFileList(dir, toParse)
android.FailIfErrored(t, errs)
_, errs = ctx.ResolveDependencies(config)
android.FailIfErrored(t, errs)
codegenCtx := NewCodegenContext(config, *ctx.Context, Bp2Build)
// For each directory, test that the expected number of generated targets is correct.
for dir, expectedCount := range testCase.expectedCount {
bazelTargets := generateBazelTargetsForDir(codegenCtx, dir)
if actualCount := len(bazelTargets); actualCount != expectedCount {
t.Fatalf(
"%s: Expected %d bazel target for %s package, got %d",
testCase.description,
expectedCount,
dir,
actualCount)
}
}
}
}
func TestCombineBuildFilesBp2buildTargets(t *testing.T) {
testCases := []struct {
description string
moduleTypeUnderTest string
moduleTypeUnderTestFactory android.ModuleFactory
moduleTypeUnderTestBp2BuildMutator func(android.TopDownMutatorContext)
preArchMutators []android.RegisterMutatorFunc
depsMutators []android.RegisterMutatorFunc
bp string
expectedBazelTargets []string
fs map[string]string
dir string
}{
{
description: "filegroup bazel_module.label",
moduleTypeUnderTest: "filegroup",
moduleTypeUnderTestFactory: android.FileGroupFactory,
moduleTypeUnderTestBp2BuildMutator: android.FilegroupBp2Build,
bp: `filegroup {
name: "fg_foo",
bazel_module: { label: "//other:fg_foo" },
}`,
expectedBazelTargets: []string{
`// BUILD file`,
},
fs: map[string]string{
"other/BUILD.bazel": `// BUILD file`,
},
},
{
description: "multiple bazel_module.label same BUILD",
moduleTypeUnderTest: "filegroup",
moduleTypeUnderTestFactory: android.FileGroupFactory,
moduleTypeUnderTestBp2BuildMutator: android.FilegroupBp2Build,
bp: `filegroup {
name: "fg_foo",
bazel_module: { label: "//other:fg_foo" },
}
filegroup {
name: "foo",
bazel_module: { label: "//other:foo" },
}`,
expectedBazelTargets: []string{
`// BUILD file`,
},
fs: map[string]string{
"other/BUILD.bazel": `// BUILD file`,
},
},
{
description: "filegroup bazel_module.label and bp2build",
moduleTypeUnderTest: "filegroup",
moduleTypeUnderTestFactory: android.FileGroupFactory,
moduleTypeUnderTestBp2BuildMutator: android.FilegroupBp2Build,
bp: `filegroup {
name: "fg_foo",
bazel_module: {
label: "//other:fg_foo",
bp2build_available: true,
},
}`,
expectedBazelTargets: []string{
`filegroup(
name = "fg_foo",
)`,
`// BUILD file`,
},
fs: map[string]string{
"other/BUILD.bazel": `// BUILD file`,
},
},
{
description: "filegroup bazel_module.label and filegroup bp2build",
moduleTypeUnderTest: "filegroup",
moduleTypeUnderTestFactory: android.FileGroupFactory,
moduleTypeUnderTestBp2BuildMutator: android.FilegroupBp2Build,
bp: `filegroup {
name: "fg_foo",
bazel_module: {
label: "//other:fg_foo",
},
}
filegroup {
name: "fg_bar",
bazel_module: {
bp2build_available: true,
},
}`,
expectedBazelTargets: []string{
`filegroup(
name = "fg_bar",
)`,
`// BUILD file`,
},
fs: map[string]string{
"other/BUILD.bazel": `// BUILD file`,
},
},
}
dir := "."
for _, testCase := range testCases {
t.Run(testCase.description, func(t *testing.T) {
fs := make(map[string][]byte)
toParse := []string{
"Android.bp",
}
for f, content := range testCase.fs {
if strings.HasSuffix(f, "Android.bp") {
toParse = append(toParse, f)
}
fs[f] = []byte(content)
}
config := android.TestConfig(buildDir, nil, testCase.bp, fs)
ctx := android.NewTestContext(config)
ctx.RegisterModuleType(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestFactory)
for _, m := range testCase.depsMutators {
ctx.DepsBp2BuildMutators(m)
}
ctx.RegisterBp2BuildMutator(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestBp2BuildMutator)
ctx.RegisterForBazelConversion()
_, errs := ctx.ParseFileList(dir, toParse)
if errored(t, testCase.description, errs) {
return
}
_, errs = ctx.ResolveDependencies(config)
if errored(t, testCase.description, errs) {
return
}
checkDir := dir
if testCase.dir != "" {
checkDir = testCase.dir
}
bazelTargets := generateBazelTargetsForDir(NewCodegenContext(config, *ctx.Context, Bp2Build), checkDir)
bazelTargets.sort()
actualCount := len(bazelTargets)
expectedCount := len(testCase.expectedBazelTargets)
if actualCount != expectedCount {
t.Errorf("Expected %d bazel target, got %d\n%s", expectedCount, actualCount, bazelTargets)
}
if !strings.Contains(bazelTargets.String(), "Section: Handcrafted targets. ") {
t.Errorf("Expected string representation of bazelTargets to contain handcrafted section header.")
}
for i, target := range bazelTargets {
actualContent := target.content
expectedContent := testCase.expectedBazelTargets[i]
if expectedContent != actualContent {
t.Errorf(
"Expected generated Bazel target to be '%s', got '%s'",
expectedContent,
actualContent,
)
}
}
})
}
}
func TestGlobExcludeSrcs(t *testing.T) {
testCases := []struct {
description string
moduleTypeUnderTest string
moduleTypeUnderTestFactory android.ModuleFactory
moduleTypeUnderTestBp2BuildMutator func(android.TopDownMutatorContext)
bp string
expectedBazelTargets []string
fs map[string]string
dir string
}{
{
description: "filegroup top level exclude_srcs",
moduleTypeUnderTest: "filegroup",
moduleTypeUnderTestFactory: android.FileGroupFactory,
moduleTypeUnderTestBp2BuildMutator: android.FilegroupBp2Build,
bp: `filegroup {
name: "fg_foo",
srcs: ["**/*.txt"],
exclude_srcs: ["c.txt"],
bazel_module: { bp2build_available: true },
}`,
expectedBazelTargets: []string{`filegroup(
name = "fg_foo",
srcs = [
"//dir:e.txt",
"//dir:f.txt",
"a.txt",
"b.txt",
],
)`,
},
fs: map[string]string{
"a.txt": "",
"b.txt": "",
"c.txt": "",
"dir/Android.bp": "",
"dir/e.txt": "",
"dir/f.txt": "",
},
},
{
description: "filegroup in subdir exclude_srcs",
moduleTypeUnderTest: "filegroup",
moduleTypeUnderTestFactory: android.FileGroupFactory,
moduleTypeUnderTestBp2BuildMutator: android.FilegroupBp2Build,
bp: "",
dir: "dir",
fs: map[string]string{
"dir/Android.bp": `filegroup {
name: "fg_foo",
srcs: ["**/*.txt"],
exclude_srcs: ["b.txt"],
bazel_module: { bp2build_available: true },
}
`,
"dir/a.txt": "",
"dir/b.txt": "",
"dir/subdir/Android.bp": "",
"dir/subdir/e.txt": "",
"dir/subdir/f.txt": "",
},
expectedBazelTargets: []string{`filegroup(
name = "fg_foo",
srcs = [
"//dir/subdir:e.txt",
"//dir/subdir:f.txt",
"a.txt",
],
)`,
},
},
}
dir := "."
for _, testCase := range testCases {
fs := make(map[string][]byte)
toParse := []string{
"Android.bp",
}
for f, content := range testCase.fs {
if strings.HasSuffix(f, "Android.bp") {
toParse = append(toParse, f)
}
fs[f] = []byte(content)
}
config := android.TestConfig(buildDir, nil, testCase.bp, fs)
ctx := android.NewTestContext(config)
ctx.RegisterModuleType(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestFactory)
ctx.RegisterBp2BuildMutator(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestBp2BuildMutator)
ctx.RegisterForBazelConversion()
_, errs := ctx.ParseFileList(dir, toParse)
if errored(t, testCase.description, errs) {
continue
}
_, errs = ctx.ResolveDependencies(config)
if errored(t, testCase.description, errs) {
continue
}
checkDir := dir
if testCase.dir != "" {
checkDir = testCase.dir
}
bazelTargets := generateBazelTargetsForDir(NewCodegenContext(config, *ctx.Context, Bp2Build), checkDir)
if actualCount, expectedCount := len(bazelTargets), len(testCase.expectedBazelTargets); actualCount != expectedCount {
t.Errorf("%s: Expected %d bazel target, got %d\n%s", testCase.description, expectedCount, actualCount, bazelTargets)
} else {
for i, target := range bazelTargets {
if w, g := testCase.expectedBazelTargets[i], target.content; w != g {
t.Errorf(
"%s: Expected generated Bazel target to be '%s', got '%s'",
testCase.description,
w,
g,
)
}
}
}
}
}