Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ba3ea16f authored by Liz Kammer's avatar Liz Kammer
Browse files

Use handcrafted build targets in bp2build

If both bp2build_available and label are specified, label will be
preferred.

Initially, we copy the entire BUILD.bazel file. Eventually we may move
this to use bazel query for a more accurate result.

Test: go test *
Test: build/bazel/scripts/milestone-2/demo.sh full
Test: GENERATE_BAZEL_FILES=true m nothing
      edit bionic/libc/tools/BUILD.bazel
      GENERATE_BAZEL_FILES=true m nothing and verify changes picked up
Bug: 180516554
Change-Id: I43025583300e6b10d2c18032cd4a76237b578d59
parent ca5e611e
Loading
Loading
Loading
Loading
+64 −5
Original line number Diff line number Diff line
@@ -14,19 +14,46 @@

package android

import "android/soong/bazel"
import (
	"fmt"
	"io/ioutil"
	"path/filepath"
	"strings"

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

type bazelModuleProperties struct {
	// The label of the Bazel target replacing this Soong module. When run in conversion mode, this
	// will import the handcrafted build target into the autogenerated file. Note: this may result in
	// a conflict due to duplicate targets if bp2build_available is also set.
	Label *string

	// If true, bp2build will generate the converted Bazel target for this module. Note: this may
	// cause a conflict due to the duplicate targets if label is also set.
	Bp2build_available bool
}

// Properties contains common module properties for Bazel migration purposes.
type properties struct {
	// In USE_BAZEL_ANALYSIS=1 mode, this represents the Bazel target replacing
	// this Soong module.
	Bazel_module bazelModuleProperties
}

// BazelModuleBase contains the property structs with metadata for modules which can be converted to
// Bazel.
type BazelModuleBase struct {
	bazelProperties bazel.Properties
	bazelProperties properties
}

// Bazelable is specifies the interface for modules that can be converted to Bazel.
type Bazelable interface {
	bazelProps() *bazel.Properties
	bazelProps() *properties
	HasHandcraftedLabel() bool
	GetBazelLabel() string
	ConvertWithBp2build() bool
	GetBazelBuildFileContents(c Config, path, name string) (string, error)
}

// BazelModule is a lightweight wrapper interface around Module for Bazel-convertible modules.
@@ -42,16 +69,48 @@ func InitBazelModule(module BazelModule) {
}

// bazelProps returns the Bazel properties for the given BazelModuleBase.
func (b *BazelModuleBase) bazelProps() *bazel.Properties {
func (b *BazelModuleBase) bazelProps() *properties {
	return &b.bazelProperties
}

// HasHandcraftedLabel returns whether this module has a handcrafted Bazel label.
func (b *BazelModuleBase) HasHandcraftedLabel() bool {
	return b.bazelProperties.Bazel_module.Label != nil
}

// HandcraftedLabel returns the handcrafted label for this module, or empty string if there is none
func (b *BazelModuleBase) HandcraftedLabel() string {
	return proptools.String(b.bazelProperties.Bazel_module.Label)
}

// GetBazelLabel returns the Bazel label for the given BazelModuleBase.
func (b *BazelModuleBase) GetBazelLabel() string {
	return b.bazelProperties.Bazel_module.Label
	return proptools.String(b.bazelProperties.Bazel_module.Label)
}

// ConvertWithBp2build returns whether the given BazelModuleBase should be converted with bp2build.
func (b *BazelModuleBase) ConvertWithBp2build() bool {
	return b.bazelProperties.Bazel_module.Bp2build_available
}

// GetBazelBuildFileContents returns the file contents of a hand-crafted BUILD file if available or
// an error if there are errors reading the file.
// TODO(b/181575318): currently we append the whole BUILD file, let's change that to do
// something more targeted based on the rule type and target.
func (b *BazelModuleBase) GetBazelBuildFileContents(c Config, path, name string) (string, error) {
	if !strings.Contains(b.GetBazelLabel(), path) {
		return "", fmt.Errorf("%q not found in bazel_module.label %q", path, b.GetBazelLabel())
	}
	name = filepath.Join(path, name)
	f, err := c.fs.Open(name)
	if err != nil {
		return "", err
	}
	defer f.Close()

	data, err := ioutil.ReadAll(f)
	if err != nil {
		return "", err
	}
	return string(data[:]), nil
}
+0 −15
Original line number Diff line number Diff line
@@ -19,21 +19,6 @@ import (
	"sort"
)

type bazelModuleProperties struct {
	// The label of the Bazel target replacing this Soong module.
	Label string

	// If true, bp2build will generate the converted Bazel target for this module.
	Bp2build_available bool
}

// Properties contains common module properties for Bazel migration purposes.
type Properties struct {
	// In USE_BAZEL_ANALYSIS=1 mode, this represents the Bazel target replacing
	// this Soong module.
	Bazel_module bazelModuleProperties
}

// BazelTargetModuleProperties contain properties and metadata used for
// Blueprint to BUILD file conversion.
type BazelTargetModuleProperties struct {
+1 −0
Original line number Diff line number Diff line
@@ -11,6 +11,7 @@ bootstrap_go_package {
        "build_conversion.go",
        "bzl_conversion.go",
        "configurability.go",
        "constants.go",
        "conversion.go",
        "metrics.go",
    ],
+1 −1
Original line number Diff line number Diff line
@@ -22,7 +22,7 @@ import (

// The Bazel bp2build code generator is responsible for writing .bzl files that are equivalent to
// Android.bp files that are capable of being built with Bazel.
func Codegen(ctx CodegenContext) CodegenMetrics {
func Codegen(ctx *CodegenContext) CodegenMetrics {
	outputDir := android.PathForOutput(ctx, "bp2build")
	android.RemoveAllOutputDir(outputDir)

+73 −25
Original line number Diff line number Diff line
@@ -102,6 +102,7 @@ type CodegenContext struct {
	config         android.Config
	context        android.Context
	mode           CodegenMode
	additionalDeps []string
}

func (c *CodegenContext) Mode() CodegenMode {
@@ -137,14 +138,26 @@ func (mode CodegenMode) String() string {
	}
}

func (ctx CodegenContext) AddNinjaFileDeps(...string) {}
func (ctx CodegenContext) Config() android.Config     { return ctx.config }
func (ctx CodegenContext) Context() android.Context   { return ctx.context }
// AddNinjaFileDeps adds dependencies on the specified files to be added to the ninja manifest. The
// primary builder will be rerun whenever the specified files are modified. Allows us to fulfill the
// PathContext interface in order to add dependencies on hand-crafted BUILD files. Note: must also
// call AdditionalNinjaDeps and add them manually to the ninja file.
func (ctx *CodegenContext) AddNinjaFileDeps(deps ...string) {
	ctx.additionalDeps = append(ctx.additionalDeps, deps...)
}

// AdditionalNinjaDeps returns additional ninja deps added by CodegenContext
func (ctx *CodegenContext) AdditionalNinjaDeps() []string {
	return ctx.additionalDeps
}

func (ctx *CodegenContext) Config() android.Config   { return ctx.config }
func (ctx *CodegenContext) Context() android.Context { return ctx.context }

// NewCodegenContext creates a wrapper context that conforms to PathContext for
// writing BUILD files in the output directory.
func NewCodegenContext(config android.Config, context android.Context, mode CodegenMode) CodegenContext {
	return CodegenContext{
func NewCodegenContext(config android.Config, context android.Context, mode CodegenMode) *CodegenContext {
	return &CodegenContext{
		context: context,
		config:  config,
		mode:    mode,
@@ -163,12 +176,14 @@ func propsToAttributes(props map[string]string) string {
	return attributes
}

func GenerateBazelTargets(ctx CodegenContext) (map[string]BazelTargets, CodegenMetrics) {
func GenerateBazelTargets(ctx *CodegenContext) (map[string]BazelTargets, CodegenMetrics) {
	buildFileToTargets := make(map[string]BazelTargets)
	buildFileToAppend := make(map[string]bool)

	// Simple metrics tracking for bp2build
	totalModuleCount := 0
	ruleClassCount := make(map[string]int)
	metrics := CodegenMetrics{
		RuleClassCount: make(map[string]int),
	}

	bpCtx := ctx.Context()
	bpCtx.VisitAllModules(func(m blueprint.Module) {
@@ -177,13 +192,29 @@ func GenerateBazelTargets(ctx CodegenContext) (map[string]BazelTargets, CodegenM

		switch ctx.Mode() {
		case Bp2Build:
			if b, ok := m.(android.BazelTargetModule); !ok {
				// Only include regular Soong modules (non-BazelTargetModules) into the total count.
				totalModuleCount += 1
			if b, ok := m.(android.Bazelable); ok && b.HasHandcraftedLabel() {
				metrics.handCraftedTargetCount += 1
				metrics.TotalModuleCount += 1
				pathToBuildFile := getBazelPackagePath(b)
				// We are using the entire contents of handcrafted build file, so if multiple targets within
				// a package have handcrafted targets, we only want to include the contents one time.
				if _, exists := buildFileToAppend[pathToBuildFile]; exists {
					return
				}
				var err error
				t, err = getHandcraftedBuildContent(ctx, b, pathToBuildFile)
				if err != nil {
					panic(fmt.Errorf("Error converting %s: %s", bpCtx.ModuleName(m), err))
				}
				// TODO(b/181575318): currently we append the whole BUILD file, let's change that to do
				// something more targeted based on the rule type and target
				buildFileToAppend[pathToBuildFile] = true
			} else if btm, ok := m.(android.BazelTargetModule); ok {
				t = generateBazelTarget(bpCtx, m, btm)
				metrics.RuleClassCount[t.ruleClass] += 1
			} else {
				t = generateBazelTarget(bpCtx, m, b)
				ruleClassCount[t.ruleClass] += 1
				metrics.TotalModuleCount += 1
				return
			}
		case QueryView:
			// Blocklist certain module types from being generated.
@@ -200,17 +231,34 @@ func GenerateBazelTargets(ctx CodegenContext) (map[string]BazelTargets, CodegenM
		buildFileToTargets[dir] = append(buildFileToTargets[dir], t)
	})

	metrics := CodegenMetrics{
		TotalModuleCount: totalModuleCount,
		RuleClassCount:   ruleClassCount,
	return buildFileToTargets, metrics
}

	return buildFileToTargets, metrics
func getBazelPackagePath(b android.Bazelable) string {
	label := b.GetBazelLabel()
	pathToBuildFile := strings.TrimPrefix(label, "//")
	pathToBuildFile = strings.Split(pathToBuildFile, ":")[0]
	return pathToBuildFile
}

func getHandcraftedBuildContent(ctx *CodegenContext, b android.Bazelable, pathToBuildFile string) (BazelTarget, error) {
	p := android.ExistentPathForSource(ctx, pathToBuildFile, HandcraftedBuildFileName)
	if !p.Valid() {
		return BazelTarget{}, fmt.Errorf("Could not find file %q for handcrafted target.", pathToBuildFile)
	}
	c, err := b.GetBazelBuildFileContents(ctx.Config(), pathToBuildFile, HandcraftedBuildFileName)
	if err != nil {
		return BazelTarget{}, err
	}
	// TODO(b/181575318): once this is more targeted, we need to include name, rule class, etc
	return BazelTarget{
		content: c,
	}, nil
}

func generateBazelTarget(ctx bpToBuildContext, m blueprint.Module, b android.BazelTargetModule) BazelTarget {
	ruleClass := b.RuleClass()
	bzlLoadLocation := b.BzlLoadLocation()
func generateBazelTarget(ctx bpToBuildContext, m blueprint.Module, btm android.BazelTargetModule) BazelTarget {
	ruleClass := btm.RuleClass()
	bzlLoadLocation := btm.BzlLoadLocation()

	// extract the bazel attributes from the module.
	props := getBuildProperties(ctx, m)
Loading