From de623294fe87ca64c8afe3937bb75eabc6bf43cc Mon Sep 17 00:00:00 2001 From: Spandan Das Date: Wed, 14 Jun 2023 21:30:38 +0000 Subject: [PATCH] Partial bp2build conversion of blueprint_go_binary This module type does not implement android.Module, and therefore we cannot write a conventional bp2build converter for this module type. Instead this has been special-cased inside bp2build/build_conversion.go. There is one major deviation between Soong and Bazel's go_binary/go_library. Soong collects the deps in the transitve closure and puts them on compile/link paths. Bazel OTOH, requires the direct imports to be listed in deps of the binary explicitly (AFAIK). Since bp2build cannot determine the list of direct imports from the list of transitive deps, put all the transitive deps in `deps` Test: unit tests Test: TH Bug: 284483729 Change-Id: I004aaf8607fef1697a0d9e7d018ad657b67778ac --- bp2build/build_conversion.go | 68 ++++++++++++++++++++++++++++++++-- bp2build/go_conversion_test.go | 37 ++++++++++++++++++ 2 files changed, 102 insertions(+), 3 deletions(-) diff --git a/bp2build/build_conversion.go b/bp2build/build_conversion.go index e1252b2d8..265a368ce 100644 --- a/bp2build/build_conversion.go +++ b/bp2build/build_conversion.go @@ -356,12 +356,23 @@ func generateBazelTargetsGoPackage(ctx *android.Context, g *bootstrap.GoPackage, ca := android.CommonAttributes{ Name: g.Name(), } + + // For this bootstrap_go_package dep chain, + // A --> B --> C ( ---> depends on) + // Soong provides the convenience of only listing B as deps of A even if a src file of A imports C + // Bazel OTOH + // 1. requires C to be listed in `deps` expllicity. + // 2. does not require C to be listed if src of A does not import C + // + // bp2build does not have sufficient info on whether C is a direct dep of A or not, so for now collect all transitive deps and add them to deps + transitiveDeps := transitiveGoDeps(g.Deps(), goModulesMap) + ga := goAttributes{ Importpath: bazel.StringAttribute{ Value: proptools.StringPtr(g.GoPkgPath()), }, Srcs: goSrcLabels(g.Srcs(), g.LinuxSrcs(), g.DarwinSrcs()), - Deps: goDepLabels(g.Deps(), goModulesMap), + Deps: goDepLabels(transitiveDeps, goModulesMap), Target_compatible_with: targetNotCompatibleWithAndroid(), } @@ -404,6 +415,55 @@ func createGoLibraryModuleMap(ctx *android.Context) nameToGoLibraryModule { return ret } +// Returns the deps in the transitive closure of a go target +func transitiveGoDeps(directDeps []string, goModulesMap nameToGoLibraryModule) []string { + allDeps := directDeps + i := 0 + for i < len(allDeps) { + curr := allDeps[i] + allDeps = append(allDeps, goModulesMap[curr].Deps...) + i += 1 + } + allDeps = android.SortedUniqueStrings(allDeps) + return allDeps +} + +func generateBazelTargetsGoBinary(ctx *android.Context, g *bootstrap.GoBinary, goModulesMap nameToGoLibraryModule) ([]BazelTarget, []error) { + ca := android.CommonAttributes{ + Name: g.Name(), + } + + // For this bootstrap_go_package dep chain, + // A --> B --> C ( ---> depends on) + // Soong provides the convenience of only listing B as deps of A even if a src file of A imports C + // Bazel OTOH + // 1. requires C to be listed in `deps` expllicity. + // 2. does not require C to be listed if src of A does not import C + // + // bp2build does not have sufficient info on whether C is a direct dep of A or not, so for now collect all transitive deps and add them to deps + transitiveDeps := transitiveGoDeps(g.Deps(), goModulesMap) + + ga := goAttributes{ + Srcs: goSrcLabels(g.Srcs(), g.LinuxSrcs(), g.DarwinSrcs()), + Deps: goDepLabels(transitiveDeps, goModulesMap), + Target_compatible_with: targetNotCompatibleWithAndroid(), + } + + bin := goBazelTarget{ + targetName: g.Name(), + targetPackage: ctx.ModuleDir(g), + bazelRuleClass: "go_binary", + bazelRuleLoadLocation: "@io_bazel_rules_go//go:def.bzl", + bazelAttributes: []interface{}{&ca, &ga}, + } + // TODO - b/284483729: Create go_test target from testSrcs + binTarget, err := generateBazelTarget(ctx, bin) + if err != nil { + return []BazelTarget{}, []error{err} + } + return []BazelTarget{binTarget}, nil +} + func GenerateBazelTargets(ctx *CodegenContext, generateFilegroups bool) (conversionResults, []error) { buildFileToTargets := make(map[string]BazelTargets) @@ -496,8 +556,10 @@ func GenerateBazelTargets(ctx *CodegenContext, generateFilegroups bool) (convers targets, targetErrs = generateBazelTargetsGoPackage(bpCtx, glib, nameToGoLibMap) errs = append(errs, targetErrs...) metrics.IncrementRuleClassCount("go_library") - } else if _, ok := m.(*bootstrap.GoBinary); ok { - // TODO - b/284483729: Create bazel targets for go binaries + } else if gbin, ok := m.(*bootstrap.GoBinary); ok { + targets, targetErrs = generateBazelTargetsGoBinary(bpCtx, gbin, nameToGoLibMap) + errs = append(errs, targetErrs...) + metrics.IncrementRuleClassCount("go_binary") } else { metrics.AddUnconvertedModule(m, moduleType, dir, android.UnconvertedReason{ ReasonType: int(bp2build_metrics_proto.UnconvertedReasonType_TYPE_UNSUPPORTED), diff --git a/bp2build/go_conversion_test.go b/bp2build/go_conversion_test.go index 13d25137b..a872fc7db 100644 --- a/bp2build/go_conversion_test.go +++ b/bp2build/go_conversion_test.go @@ -87,3 +87,40 @@ bootstrap_go_package { )}, }) } + +func TestConvertGoBinaryWithTransitiveDeps(t *testing.T) { + bp := ` +blueprint_go_binary { + name: "foo", + srcs: ["main.go"], + deps: ["bar"], +} +` + depBp := ` +bootstrap_go_package { + name: "bar", + deps: ["baz"], +} +bootstrap_go_package { + name: "baz", +} +` + t.Parallel() + runGoTests(t, Bp2buildTestCase{ + Description: "Convert blueprint_go_binary to go_binary", + Blueprint: bp, + Filesystem: map[string]string{ + "bar/Android.bp": depBp, // Put dep in Android.bp to reduce boilerplate in ExpectedBazelTargets + }, + ExpectedBazelTargets: []string{makeBazelTargetHostOrDevice("go_binary", "foo", + AttrNameToString{ + "deps": `[ + "//bar:bar", + "//bar:baz", + ]`, + "srcs": `[":main.go"]`, + }, + android.HostSupported, + )}, + }) +}