Merge changes I3ecdeaab,I43720641

* changes:
  Make BUILD file merging slightly smarter.
  bp2build/b: exit early in GENERATE_BAZEL_FILES=1.
This commit is contained in:
Jingwen Chen
2021-05-25 08:56:21 +00:00
committed by Gerrit Code Review
7 changed files with 143 additions and 71 deletions

View File

@@ -138,7 +138,6 @@ var (
// e.g. ERROR: Analysis of target '@soong_injection//mixed_builds:buildroot' failed // e.g. ERROR: Analysis of target '@soong_injection//mixed_builds:buildroot' failed
"external/bazelbuild-rules_android":/* recursive = */ true, "external/bazelbuild-rules_android":/* recursive = */ true,
"prebuilts/clang/host/linux-x86":/* recursive = */ false,
"prebuilts/sdk":/* recursive = */ false, "prebuilts/sdk":/* recursive = */ false,
"prebuilts/sdk/tools":/* recursive = */ false, "prebuilts/sdk/tools":/* recursive = */ false,
} }
@@ -155,6 +154,7 @@ var (
"external/fmtlib": Bp2BuildDefaultTrueRecursively, "external/fmtlib": Bp2BuildDefaultTrueRecursively,
"external/arm-optimized-routines": Bp2BuildDefaultTrueRecursively, "external/arm-optimized-routines": Bp2BuildDefaultTrueRecursively,
"external/scudo": Bp2BuildDefaultTrueRecursively, "external/scudo": Bp2BuildDefaultTrueRecursively,
"prebuilts/clang/host/linux-x86": Bp2BuildDefaultTrueRecursively,
} }
// Per-module denylist to always opt modules out of both bp2build and mixed builds. // Per-module denylist to always opt modules out of both bp2build and mixed builds.

View File

@@ -19,6 +19,7 @@ import (
"android/soong/bazel" "android/soong/bazel"
"fmt" "fmt"
"reflect" "reflect"
"sort"
"strings" "strings"
"github.com/google/blueprint" "github.com/google/blueprint"
@@ -34,6 +35,7 @@ type BazelTarget struct {
content string content string
ruleClass string ruleClass string
bzlLoadLocation string bzlLoadLocation string
handcrafted bool
} }
// IsLoadedFromStarlark determines if the BazelTarget's rule class is loaded from a .bzl file, // IsLoadedFromStarlark determines if the BazelTarget's rule class is loaded from a .bzl file,
@@ -45,12 +47,47 @@ func (t BazelTarget) IsLoadedFromStarlark() bool {
// BazelTargets is a typedef for a slice of BazelTarget objects. // BazelTargets is a typedef for a slice of BazelTarget objects.
type BazelTargets []BazelTarget type BazelTargets []BazelTarget
// HasHandcraftedTargetsreturns true if a set of bazel targets contain
// handcrafted ones.
func (targets BazelTargets) hasHandcraftedTargets() bool {
for _, target := range targets {
if target.handcrafted {
return true
}
}
return false
}
// sort a list of BazelTargets in-place, by name, and by generated/handcrafted types.
func (targets BazelTargets) sort() {
sort.Slice(targets, func(i, j int) bool {
if targets[i].handcrafted != targets[j].handcrafted {
// Handcrafted targets will be generated after the bp2build generated targets.
return targets[j].handcrafted
}
// This will cover all bp2build generated targets.
return targets[i].name < targets[j].name
})
}
// String returns the string representation of BazelTargets, without load // String returns the string representation of BazelTargets, without load
// statements (use LoadStatements for that), since the targets are usually not // statements (use LoadStatements for that), since the targets are usually not
// adjacent to the load statements at the top of the BUILD file. // adjacent to the load statements at the top of the BUILD file.
func (targets BazelTargets) String() string { func (targets BazelTargets) String() string {
var res string var res string
for i, target := range targets { for i, target := range targets {
// There is only at most 1 handcrafted "target", because its contents
// represent the entire BUILD file content from the tree. See
// build_conversion.go#getHandcraftedBuildContent for more information.
//
// Add a header to make it easy to debug where the handcrafted targets
// are in a generated BUILD file.
if target.handcrafted {
res += "# -----------------------------\n"
res += "# Section: Handcrafted targets. \n"
res += "# -----------------------------\n\n"
}
res += target.content res += target.content
if i != len(targets)-1 { if i != len(targets)-1 {
res += "\n\n" res += "\n\n"
@@ -267,7 +304,8 @@ func getHandcraftedBuildContent(ctx *CodegenContext, b android.Bazelable, pathTo
} }
// TODO(b/181575318): once this is more targeted, we need to include name, rule class, etc // TODO(b/181575318): once this is more targeted, we need to include name, rule class, etc
return BazelTarget{ return BazelTarget{
content: c, content: c,
handcrafted: true,
}, nil }, nil
} }
@@ -294,6 +332,7 @@ func generateBazelTarget(ctx bpToBuildContext, m blueprint.Module, btm android.B
targetName, targetName,
attributes, attributes,
), ),
handcrafted: false,
} }
} }

View File

@@ -1452,53 +1452,61 @@ filegroup {
dir := "." dir := "."
for _, testCase := range testCases { for _, testCase := range testCases {
fs := make(map[string][]byte) t.Run(testCase.description, func(t *testing.T) {
toParse := []string{ fs := make(map[string][]byte)
"Android.bp", toParse := []string{
} "Android.bp",
for f, content := range testCase.fs {
if strings.HasSuffix(f, "Android.bp") {
toParse = append(toParse, f)
} }
fs[f] = []byte(content) for f, content := range testCase.fs {
} if strings.HasSuffix(f, "Android.bp") {
config := android.TestConfig(buildDir, nil, testCase.bp, fs) toParse = append(toParse, f)
ctx := android.NewTestContext(config) }
ctx.RegisterModuleType(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestFactory) fs[f] = []byte(content)
for _, m := range testCase.depsMutators { }
ctx.DepsBp2BuildMutators(m) config := android.TestConfig(buildDir, nil, testCase.bp, fs)
} ctx := android.NewTestContext(config)
ctx.RegisterBp2BuildMutator(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestBp2BuildMutator) ctx.RegisterModuleType(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestFactory)
ctx.RegisterForBazelConversion() for _, m := range testCase.depsMutators {
ctx.DepsBp2BuildMutators(m)
}
ctx.RegisterBp2BuildMutator(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestBp2BuildMutator)
ctx.RegisterForBazelConversion()
_, errs := ctx.ParseFileList(dir, toParse) _, errs := ctx.ParseFileList(dir, toParse)
if errored(t, testCase.description, errs) { if errored(t, testCase.description, errs) {
continue return
} }
_, errs = ctx.ResolveDependencies(config) _, errs = ctx.ResolveDependencies(config)
if errored(t, testCase.description, errs) { if errored(t, testCase.description, errs) {
continue return
} }
checkDir := dir checkDir := dir
if testCase.dir != "" { if testCase.dir != "" {
checkDir = testCase.dir checkDir = testCase.dir
} }
bazelTargets := generateBazelTargetsForDir(NewCodegenContext(config, *ctx.Context, Bp2Build), checkDir) bazelTargets := generateBazelTargetsForDir(NewCodegenContext(config, *ctx.Context, Bp2Build), checkDir)
if actualCount, expectedCount := len(bazelTargets), len(testCase.expectedBazelTargets); actualCount != expectedCount { bazelTargets.sort()
t.Errorf("%s: Expected %d bazel target, got %d\n%s", testCase.description, expectedCount, actualCount, bazelTargets) actualCount := len(bazelTargets)
} else { 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 { for i, target := range bazelTargets {
if w, g := testCase.expectedBazelTargets[i], target.content; w != g { actualContent := target.content
expectedContent := testCase.expectedBazelTargets[i]
if expectedContent != actualContent {
t.Errorf( t.Errorf(
"%s: Expected generated Bazel target to be '%s', got '%s'", "Expected generated Bazel target to be '%s', got '%s'",
testCase.description, expectedContent,
w, actualContent,
g,
) )
} }
} }
} })
} }
} }

View File

@@ -5,7 +5,6 @@ import (
"android/soong/cc/config" "android/soong/cc/config"
"fmt" "fmt"
"reflect" "reflect"
"sort"
"strings" "strings"
"github.com/google/blueprint/proptools" "github.com/google/blueprint/proptools"
@@ -64,22 +63,28 @@ func createBuildFiles(buildToTargets map[string]BazelTargets, mode CodegenMode)
continue continue
} }
targets := buildToTargets[dir] targets := buildToTargets[dir]
sort.Slice(targets, func(i, j int) bool { targets.sort()
// this will cover all bp2build generated targets
if targets[i].name < targets[j].name { var content string
return true
}
// give a strict ordering to content from hand-crafted targets
return targets[i].content < targets[j].content
})
content := soongModuleLoad
if mode == Bp2Build { if mode == Bp2Build {
content = `# This file was automatically generated by bp2build for the Bazel migration project. content = `# READ THIS FIRST:
# Feel free to edit or test it, but do *not* check it into your version control system.` # This file was automatically generated by bp2build for the Bazel migration project.
content += "\n\n" # Feel free to edit or test it, but do *not* check it into your version control system.
content += "package(default_visibility = [\"//visibility:public\"])" `
content += "\n\n" if targets.hasHandcraftedTargets() {
// For BUILD files with both handcrafted and generated targets,
// don't hardcode actual content, like package() declarations.
// Leave that responsibility to the checked-in BUILD file
// instead.
content += `# This file contains generated targets and handcrafted targets that are manually managed in the source tree.`
} else {
// For fully-generated BUILD files, hardcode the default visibility.
content += "package(default_visibility = [\"//visibility:public\"])"
}
content += "\n"
content += targets.LoadStatements() content += targets.LoadStatements()
} else if mode == QueryView {
content = soongModuleLoad
} }
if content != "" { if content != "" {
// If there are load statements, add a couple of newlines. // If there are load statements, add a couple of newlines.

View File

@@ -175,6 +175,9 @@ func writeJsonModuleGraph(configuration android.Config, ctx *android.Context, pa
writeFakeNinjaFile(extraNinjaDeps, configuration.BuildDir()) writeFakeNinjaFile(extraNinjaDeps, configuration.BuildDir())
} }
// doChosenActivity runs Soong for a specific activity, like bp2build, queryview
// or the actual Soong build for the build.ninja file. Returns the top level
// output file of the specific activity.
func doChosenActivity(configuration android.Config, extraNinjaDeps []string) string { func doChosenActivity(configuration android.Config, extraNinjaDeps []string) string {
bazelConversionRequested := bp2buildMarker != "" bazelConversionRequested := bp2buildMarker != ""
mixedModeBuild := configuration.BazelContext.BazelEnabled() mixedModeBuild := configuration.BazelContext.BazelEnabled()
@@ -187,11 +190,7 @@ func doChosenActivity(configuration android.Config, extraNinjaDeps []string) str
// Run the alternate pipeline of bp2build mutators and singleton to convert // Run the alternate pipeline of bp2build mutators and singleton to convert
// Blueprint to BUILD files before everything else. // Blueprint to BUILD files before everything else.
runBp2Build(configuration, extraNinjaDeps) runBp2Build(configuration, extraNinjaDeps)
if bp2buildMarker != "" { return bp2buildMarker
return bp2buildMarker
} else {
return bootstrap.CmdlineArgs.OutFile
}
} }
ctx := newContext(configuration, prepareBuildActions) ctx := newContext(configuration, prepareBuildActions)
@@ -327,13 +326,13 @@ func writeFakeNinjaFile(extraNinjaDeps []string, buildDir string) {
ninjaFileName := "build.ninja" ninjaFileName := "build.ninja"
ninjaFile := shared.JoinPath(topDir, buildDir, ninjaFileName) ninjaFile := shared.JoinPath(topDir, buildDir, ninjaFileName)
ninjaFileD := shared.JoinPath(topDir, buildDir, ninjaFileName) ninjaFileD := shared.JoinPath(topDir, buildDir, ninjaFileName+".d")
// A workaround to create the 'nothing' ninja target so `m nothing` works, // A workaround to create the 'nothing' ninja target so `m nothing` works,
// since bp2build runs without Kati, and the 'nothing' target is declared in // since bp2build runs without Kati, and the 'nothing' target is declared in
// a Makefile. // a Makefile.
ioutil.WriteFile(ninjaFile, []byte("build nothing: phony\n phony_output = true\n"), 0666) ioutil.WriteFile(ninjaFile, []byte("build nothing: phony\n phony_output = true\n"), 0666)
ioutil.WriteFile(ninjaFileD, ioutil.WriteFile(ninjaFileD,
[]byte(fmt.Sprintf("%s: \\\n %s\n", ninjaFileName, extraNinjaDepsString)), []byte(fmt.Sprintf("%s: \\\n %s\n", ninjaFile, extraNinjaDepsString)),
0666) 0666)
} }
@@ -520,9 +519,14 @@ func runBp2Build(configuration android.Config, extraNinjaDeps []string) {
os.Exit(1) os.Exit(1)
} }
if bp2buildMarker != "" { // Create an empty bp2build marker file.
touch(shared.JoinPath(topDir, bp2buildMarker)) touch(shared.JoinPath(topDir, bp2buildMarker))
} else {
writeFakeNinjaFile(extraNinjaDeps, codegenContext.Config().BuildDir()) // bp2build *always* writes a fake Ninja file containing just the nothing
} // phony target if it ever re-runs. This allows bp2build to exit early with
// GENERATE_BAZEL_FILES=1 m nothing.
//
// If bp2build is invoked as part of an integrated mixed build, the fake
// build.ninja file will be rewritten later into the real file anyway.
writeFakeNinjaFile(extraNinjaDeps, codegenContext.Config().BuildDir())
} }

View File

@@ -493,6 +493,21 @@ function test_bp2build_smoke {
[[ -e out/soong/workspace ]] || fail "Bazel workspace not created" [[ -e out/soong/workspace ]] || fail "Bazel workspace not created"
} }
function test_bp2build_generates_fake_ninja_file {
setup
create_mock_bazel
run_bp2build
if [[ ! -f "./out/soong/build.ninja" ]]; then
fail "./out/soong/build.ninja was not generated"
fi
if ! grep "build nothing: phony" "./out/soong/build.ninja"; then
fail "missing phony nothing target in out/soong/build.ninja"
fi
}
function test_bp2build_add_android_bp { function test_bp2build_add_android_bp {
setup setup
@@ -678,6 +693,7 @@ test_glob_during_bootstrapping
test_soong_build_rerun_iff_environment_changes test_soong_build_rerun_iff_environment_changes
test_dump_json_module_graph test_dump_json_module_graph
test_bp2build_smoke test_bp2build_smoke
test_bp2build_generates_fake_ninja_file
test_bp2build_null_build test_bp2build_null_build
test_bp2build_add_android_bp test_bp2build_add_android_bp
test_bp2build_add_to_glob test_bp2build_add_to_glob

View File

@@ -155,9 +155,9 @@ func bootstrapBlueprint(ctx Context, config Config, integratedBp2Build bool) {
Outputs: []string{bp2BuildMarkerFile}, Outputs: []string{bp2BuildMarkerFile},
Args: bp2buildArgs, Args: bp2buildArgs,
} }
args.PrimaryBuilderInvocations = []bootstrap.PrimaryBuilderInvocation{ args.PrimaryBuilderInvocations = []bootstrap.PrimaryBuilderInvocation{bp2buildInvocation}
bp2buildInvocation, if config.bazelBuildMode() == mixedBuild {
mainSoongBuildInvocation, args.PrimaryBuilderInvocations = append(args.PrimaryBuilderInvocations, mainSoongBuildInvocation)
} }
} else { } else {
args.PrimaryBuilderInvocations = []bootstrap.PrimaryBuilderInvocation{mainSoongBuildInvocation} args.PrimaryBuilderInvocations = []bootstrap.PrimaryBuilderInvocation{mainSoongBuildInvocation}