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

Commit 8733a89c authored by Cole Faust's avatar Cole Faust Committed by Gerrit Code Review
Browse files

Merge "Refactor python rules"

parents 9002605d 4d247e6f
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -1657,7 +1657,7 @@ func apexFileForRustLibrary(ctx android.BaseModuleContext, rustm *rust.Module) a
	return newApexFile(ctx, fileToCopy, androidMkModuleName, dirInApex, nativeSharedLib, rustm)
}

func apexFileForPyBinary(ctx android.BaseModuleContext, py *python.Module) apexFile {
func apexFileForPyBinary(ctx android.BaseModuleContext, py *python.PythonBinaryModule) apexFile {
	dirInApex := "bin"
	fileToCopy := py.HostToolPath().Path()
	return newApexFile(ctx, fileToCopy, py.BaseModuleName(), dirInApex, pyBinary, py)
@@ -2147,7 +2147,7 @@ func (a *apexBundle) depVisitor(vctx *visitorContext, ctx android.ModuleContext,
			case *cc.Module:
				vctx.filesInfo = append(vctx.filesInfo, apexFileForExecutable(ctx, ch))
				return true // track transitive dependencies
			case *python.Module:
			case *python.PythonBinaryModule:
				if ch.HostToolPath().Valid() {
					vctx.filesInfo = append(vctx.filesInfo, apexFileForPyBinary(ctx, ch))
				}
+1 −2
Original line number Diff line number Diff line
@@ -11,11 +11,10 @@ bootstrap_go_package {
        "soong-tradefed",
    ],
    srcs: [
        "androidmk.go",
        "binary.go",
        "bp2build.go",
        "builder.go",
        "defaults.go",
        "installer.go",
        "library.go",
        "proto.go",
        "python.go",

python/androidmk.go

deleted100644 → 0
+0 −90
Original line number Diff line number Diff line
// Copyright 2017 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 python

import (
	"path/filepath"
	"strings"

	"android/soong/android"
)

type subAndroidMkProvider interface {
	AndroidMk(*Module, *android.AndroidMkEntries)
}

func (p *Module) subAndroidMk(entries *android.AndroidMkEntries, obj interface{}) {
	if p.subAndroidMkOnce == nil {
		p.subAndroidMkOnce = make(map[subAndroidMkProvider]bool)
	}
	if androidmk, ok := obj.(subAndroidMkProvider); ok {
		if !p.subAndroidMkOnce[androidmk] {
			p.subAndroidMkOnce[androidmk] = true
			androidmk.AndroidMk(p, entries)
		}
	}
}

func (p *Module) AndroidMkEntries() []android.AndroidMkEntries {
	entries := android.AndroidMkEntries{OutputFile: p.installSource}

	p.subAndroidMk(&entries, p.installer)

	return []android.AndroidMkEntries{entries}
}

func (p *binaryDecorator) AndroidMk(base *Module, entries *android.AndroidMkEntries) {
	entries.Class = "EXECUTABLES"

	entries.ExtraEntries = append(entries.ExtraEntries,
		func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
			entries.AddCompatibilityTestSuites(p.binaryProperties.Test_suites...)
		})
	base.subAndroidMk(entries, p.pythonInstaller)
}

func (p *testDecorator) AndroidMk(base *Module, entries *android.AndroidMkEntries) {
	entries.Class = "NATIVE_TESTS"

	entries.ExtraEntries = append(entries.ExtraEntries,
		func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
			entries.AddCompatibilityTestSuites(p.binaryDecorator.binaryProperties.Test_suites...)
			if p.testConfig != nil {
				entries.SetString("LOCAL_FULL_TEST_CONFIG", p.testConfig.String())
			}

			entries.SetBoolIfTrue("LOCAL_DISABLE_AUTO_GENERATE_TEST_CONFIG", !BoolDefault(p.binaryProperties.Auto_gen_config, true))

			entries.AddStrings("LOCAL_TEST_DATA", android.AndroidMkDataPaths(p.data)...)

			p.testProperties.Test_options.SetAndroidMkEntries(entries)
		})
	base.subAndroidMk(entries, p.binaryDecorator.pythonInstaller)
}

func (installer *pythonInstaller) AndroidMk(base *Module, entries *android.AndroidMkEntries) {
	entries.Required = append(entries.Required, "libc++")
	entries.ExtraEntries = append(entries.ExtraEntries,
		func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
			path, file := filepath.Split(installer.path.String())
			stem := strings.TrimSuffix(file, filepath.Ext(file))

			entries.SetString("LOCAL_MODULE_SUFFIX", filepath.Ext(file))
			entries.SetString("LOCAL_MODULE_PATH", path)
			entries.SetString("LOCAL_MODULE_STEM", stem)
			entries.AddStrings("LOCAL_SHARED_LIBRARIES", installer.androidMkSharedLibs...)
			entries.SetBool("LOCAL_CHECK_ELF_FILES", false)
		})
}
+182 −97
Original line number Diff line number Diff line
@@ -18,11 +18,12 @@ package python

import (
	"fmt"
	"path/filepath"
	"strings"

	"android/soong/android"
	"android/soong/bazel"
	"github.com/google/blueprint"

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

func init() {
@@ -33,63 +34,6 @@ func registerPythonBinaryComponents(ctx android.RegistrationContext) {
	ctx.RegisterModuleType("python_binary_host", PythonBinaryHostFactory)
}

type bazelPythonBinaryAttributes struct {
	Main           *bazel.Label
	Srcs           bazel.LabelListAttribute
	Deps           bazel.LabelListAttribute
	Python_version *string
	Imports        bazel.StringListAttribute
}

func pythonBinaryBp2Build(ctx android.TopDownMutatorContext, m *Module) {
	// TODO(b/182306917): this doesn't fully handle all nested props versioned
	// by the python version, which would have been handled by the version split
	// mutator. This is sufficient for very simple python_binary_host modules
	// under Bionic.
	py3Enabled := proptools.BoolDefault(m.properties.Version.Py3.Enabled, false)
	py2Enabled := proptools.BoolDefault(m.properties.Version.Py2.Enabled, false)
	var python_version *string
	if py3Enabled && py2Enabled {
		panic(fmt.Errorf(
			"error for '%s' module: bp2build's python_binary_host converter does not support "+
				"converting a module that is enabled for both Python 2 and 3 at the same time.", m.Name()))
	} else if py2Enabled {
		python_version = &pyVersion2
	} else {
		// do nothing, since python_version defaults to PY3.
	}

	baseAttrs := m.makeArchVariantBaseAttributes(ctx)
	attrs := &bazelPythonBinaryAttributes{
		Main:           nil,
		Srcs:           baseAttrs.Srcs,
		Deps:           baseAttrs.Deps,
		Python_version: python_version,
		Imports:        baseAttrs.Imports,
	}

	for _, propIntf := range m.GetProperties() {
		if props, ok := propIntf.(*BinaryProperties); ok {
			// main is optional.
			if props.Main != nil {
				main := android.BazelLabelForModuleSrcSingle(ctx, *props.Main)
				attrs.Main = &main
				break
			}
		}
	}

	props := bazel.BazelTargetModuleProperties{
		// Use the native py_binary rule.
		Rule_class: "py_binary",
	}

	ctx.CreateBazelTargetModule(props, android.CommonAttributes{
		Name: m.Name(),
		Data: baseAttrs.Data,
	}, attrs)
}

type BinaryProperties struct {
	// the name of the source file that is the main entry point of the program.
	// this file must also be listed in srcs.
@@ -118,52 +62,61 @@ type BinaryProperties struct {
	Auto_gen_config *bool
}

type binaryDecorator struct {
type PythonBinaryModule struct {
	PythonLibraryModule
	binaryProperties BinaryProperties

	*pythonInstaller
	// (.intermediate) module output path as installation source.
	installSource android.Path

	// Final installation path.
	installedDest android.Path

	androidMkSharedLibs []string
}

var _ android.AndroidMkEntriesProvider = (*PythonBinaryModule)(nil)
var _ android.Module = (*PythonBinaryModule)(nil)

type IntermPathProvider interface {
	IntermPathForModuleOut() android.OptionalPath
}

func NewBinary(hod android.HostOrDeviceSupported) (*Module, *binaryDecorator) {
	module := newModule(hod, android.MultilibFirst)
	decorator := &binaryDecorator{pythonInstaller: NewPythonInstaller("bin", "")}

	module.bootstrapper = decorator
	module.installer = decorator

	return module, decorator
func NewBinary(hod android.HostOrDeviceSupported) *PythonBinaryModule {
	return &PythonBinaryModule{
		PythonLibraryModule: *newModule(hod, android.MultilibFirst),
	}
}

func PythonBinaryHostFactory() android.Module {
	module, _ := NewBinary(android.HostSupported)

	android.InitBazelModule(module)

	return module.init()
	return NewBinary(android.HostSupported).init()
}

func (binary *binaryDecorator) autorun() bool {
	return BoolDefault(binary.binaryProperties.Autorun, true)
func (p *PythonBinaryModule) init() android.Module {
	p.AddProperties(&p.properties, &p.protoProperties)
	p.AddProperties(&p.binaryProperties)
	android.InitAndroidArchModule(p, p.hod, p.multilib)
	android.InitDefaultableModule(p)
	android.InitBazelModule(p)
	return p
}

func (binary *binaryDecorator) bootstrapperProps() []interface{} {
	return []interface{}{&binary.binaryProperties}
func (p *PythonBinaryModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
	p.PythonLibraryModule.GenerateAndroidBuildActions(ctx)
	p.buildBinary(ctx)
	p.installedDest = ctx.InstallFile(installDir(ctx, "bin", "", ""),
		p.installSource.Base(), p.installSource)
}

func (binary *binaryDecorator) bootstrap(ctx android.ModuleContext, actualVersion string,
	embeddedLauncher bool, srcsPathMappings []pathMapping, srcsZip android.Path,
	depsSrcsZips android.Paths) android.OptionalPath {

func (p *PythonBinaryModule) buildBinary(ctx android.ModuleContext) {
	depsSrcsZips := p.collectPathsFromTransitiveDeps(ctx)
	main := ""
	if binary.autorun() {
		main = binary.getPyMainFile(ctx, srcsPathMappings)
	if p.autorun() {
		main = p.getPyMainFile(ctx, p.srcsPathMappings)
	}

	var launcherPath android.OptionalPath
	embeddedLauncher := p.isEmbeddedLauncherEnabled()
	if embeddedLauncher {
		ctx.VisitDirectDepsWithTag(launcherTag, func(m android.Module) {
			if provider, ok := m.(IntermPathProvider); ok {
@@ -175,15 +128,137 @@ func (binary *binaryDecorator) bootstrap(ctx android.ModuleContext, actualVersio
			}
		})
	}
	binFile := registerBuildActionForParFile(ctx, embeddedLauncher, launcherPath,
		binary.getHostInterpreterName(ctx, actualVersion),
		main, binary.getStem(ctx), append(android.Paths{srcsZip}, depsSrcsZips...))
	p.installSource = registerBuildActionForParFile(ctx, embeddedLauncher, launcherPath,
		p.getHostInterpreterName(ctx, p.properties.Actual_version),
		main, p.getStem(ctx), append(android.Paths{p.srcsZip}, depsSrcsZips...))

	var sharedLibs []string
	// if embedded launcher is enabled, we need to collect the shared library dependencies of the
	// launcher
	for _, dep := range ctx.GetDirectDepsWithTag(launcherSharedLibTag) {
		sharedLibs = append(sharedLibs, ctx.OtherModuleName(dep))
	}
	p.androidMkSharedLibs = sharedLibs
}

func (p *PythonBinaryModule) AndroidMkEntries() []android.AndroidMkEntries {
	entries := android.AndroidMkEntries{OutputFile: android.OptionalPathForPath(p.installSource)}

	entries.Class = "EXECUTABLES"

	entries.ExtraEntries = append(entries.ExtraEntries,
		func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
			entries.AddCompatibilityTestSuites(p.binaryProperties.Test_suites...)
		})

	entries.Required = append(entries.Required, "libc++")
	entries.ExtraEntries = append(entries.ExtraEntries,
		func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
			path, file := filepath.Split(p.installedDest.String())
			stem := strings.TrimSuffix(file, filepath.Ext(file))

			entries.SetString("LOCAL_MODULE_SUFFIX", filepath.Ext(file))
			entries.SetString("LOCAL_MODULE_PATH", path)
			entries.SetString("LOCAL_MODULE_STEM", stem)
			entries.AddStrings("LOCAL_SHARED_LIBRARIES", p.androidMkSharedLibs...)
			entries.SetBool("LOCAL_CHECK_ELF_FILES", false)
		})

	return []android.AndroidMkEntries{entries}
}

func (p *PythonBinaryModule) DepsMutator(ctx android.BottomUpMutatorContext) {
	p.PythonLibraryModule.DepsMutator(ctx)

	return android.OptionalPathForPath(binFile)
	versionVariation := []blueprint.Variation{
		{"python_version", p.properties.Actual_version},
	}

	// If this module will be installed and has an embedded launcher, we need to add dependencies for:
	//   * standard library
	//   * launcher
	//   * shared dependencies of the launcher
	if p.isEmbeddedLauncherEnabled() {
		var stdLib string
		var launcherModule string
		// Add launcher shared lib dependencies. Ideally, these should be
		// derived from the `shared_libs` property of the launcher. However, we
		// cannot read the property at this stage and it will be too late to add
		// dependencies later.
		launcherSharedLibDeps := []string{
			"libsqlite",
		}
		// Add launcher-specific dependencies for bionic
		if ctx.Target().Os.Bionic() {
			launcherSharedLibDeps = append(launcherSharedLibDeps, "libc", "libdl", "libm")
		}
		if ctx.Target().Os == android.LinuxMusl && !ctx.Config().HostStaticBinaries() {
			launcherSharedLibDeps = append(launcherSharedLibDeps, "libc_musl")
		}

		switch p.properties.Actual_version {
		case pyVersion2:
			stdLib = "py2-stdlib"

			launcherModule = "py2-launcher"
			if p.autorun() {
				launcherModule = "py2-launcher-autorun"
			}

			launcherSharedLibDeps = append(launcherSharedLibDeps, "libc++")

		case pyVersion3:
			stdLib = "py3-stdlib"

			launcherModule = "py3-launcher"
			if p.autorun() {
				launcherModule = "py3-launcher-autorun"
			}
			if ctx.Config().HostStaticBinaries() && ctx.Target().Os == android.LinuxMusl {
				launcherModule += "-static"
			}

			if ctx.Device() {
				launcherSharedLibDeps = append(launcherSharedLibDeps, "liblog")
			}
		default:
			panic(fmt.Errorf("unknown Python Actual_version: %q for module: %q.",
				p.properties.Actual_version, ctx.ModuleName()))
		}
		ctx.AddVariationDependencies(versionVariation, pythonLibTag, stdLib)
		ctx.AddFarVariationDependencies(ctx.Target().Variations(), launcherTag, launcherModule)
		ctx.AddFarVariationDependencies(ctx.Target().Variations(), launcherSharedLibTag, launcherSharedLibDeps...)
	}
}

// HostToolPath returns a path if appropriate such that this module can be used as a host tool,
// fulfilling the android.HostToolProvider interface.
func (p *PythonBinaryModule) HostToolPath() android.OptionalPath {
	// TODO: This should only be set when building host binaries -- tests built for device would be
	// setting this incorrectly.
	return android.OptionalPathForPath(p.installedDest)
}

// OutputFiles returns output files based on given tag, returns an error if tag is unsupported.
func (p *PythonBinaryModule) OutputFiles(tag string) (android.Paths, error) {
	switch tag {
	case "":
		return android.Paths{p.installSource}, nil
	default:
		return nil, fmt.Errorf("unsupported module reference tag %q", tag)
	}
}

func (p *PythonBinaryModule) isEmbeddedLauncherEnabled() bool {
	return Bool(p.properties.Embedded_launcher)
}

func (b *PythonBinaryModule) autorun() bool {
	return BoolDefault(b.binaryProperties.Autorun, true)
}

// get host interpreter name.
func (binary *binaryDecorator) getHostInterpreterName(ctx android.ModuleContext,
func (p *PythonBinaryModule) getHostInterpreterName(ctx android.ModuleContext,
	actualVersion string) string {
	var interp string
	switch actualVersion {
@@ -200,13 +275,13 @@ func (binary *binaryDecorator) getHostInterpreterName(ctx android.ModuleContext,
}

// find main program path within runfiles tree.
func (binary *binaryDecorator) getPyMainFile(ctx android.ModuleContext,
func (p *PythonBinaryModule) getPyMainFile(ctx android.ModuleContext,
	srcsPathMappings []pathMapping) string {
	var main string
	if String(binary.binaryProperties.Main) == "" {
	if String(p.binaryProperties.Main) == "" {
		main = ctx.ModuleName() + pyExt
	} else {
		main = String(binary.binaryProperties.Main)
		main = String(p.binaryProperties.Main)
	}

	for _, path := range srcsPathMappings {
@@ -219,11 +294,21 @@ func (binary *binaryDecorator) getPyMainFile(ctx android.ModuleContext,
	return ""
}

func (binary *binaryDecorator) getStem(ctx android.ModuleContext) string {
func (p *PythonBinaryModule) getStem(ctx android.ModuleContext) string {
	stem := ctx.ModuleName()
	if String(binary.binaryProperties.Stem) != "" {
		stem = String(binary.binaryProperties.Stem)
	if String(p.binaryProperties.Stem) != "" {
		stem = String(p.binaryProperties.Stem)
	}

	return stem + String(binary.binaryProperties.Suffix)
	return stem + String(p.binaryProperties.Suffix)
}

func installDir(ctx android.ModuleContext, dir, dir64, relative string) android.InstallPath {
	if ctx.Arch().ArchType.Multilib == "lib64" && dir64 != "" {
		dir = dir64
	}
	if !ctx.Host() && ctx.Config().HasMultilibConflict(ctx.Arch().ArchType) {
		dir = filepath.Join(dir, ctx.Arch().ArchType.String())
	}
	return android.PathForModuleInstall(ctx, dir, relative)
}

python/bp2build.go

0 → 100644
+226 −0
Original line number Diff line number Diff line
// Copyright 2023 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 python

import (
	"fmt"
	"path/filepath"
	"strings"

	"github.com/google/blueprint/proptools"

	"android/soong/android"
	"android/soong/bazel"
)

type bazelPythonLibraryAttributes struct {
	Srcs         bazel.LabelListAttribute
	Deps         bazel.LabelListAttribute
	Imports      bazel.StringListAttribute
	Srcs_version *string
}

type bazelPythonProtoLibraryAttributes struct {
	Deps bazel.LabelListAttribute
}

type baseAttributes struct {
	// TODO(b/200311466): Probably not translate b/c Bazel has no good equiv
	//Pkg_path    bazel.StringAttribute
	// TODO: Related to Pkg_bath and similarLy gated
	//Is_internal bazel.BoolAttribute
	// Combines Srcs and Exclude_srcs
	Srcs bazel.LabelListAttribute
	Deps bazel.LabelListAttribute
	// Combines Data and Java_data (invariant)
	Data    bazel.LabelListAttribute
	Imports bazel.StringListAttribute
}

func (m *PythonLibraryModule) makeArchVariantBaseAttributes(ctx android.TopDownMutatorContext) baseAttributes {
	var attrs baseAttributes
	archVariantBaseProps := m.GetArchVariantProperties(ctx, &BaseProperties{})
	for axis, configToProps := range archVariantBaseProps {
		for config, props := range configToProps {
			if baseProps, ok := props.(*BaseProperties); ok {
				attrs.Srcs.SetSelectValue(axis, config,
					android.BazelLabelForModuleSrcExcludes(ctx, baseProps.Srcs, baseProps.Exclude_srcs))
				attrs.Deps.SetSelectValue(axis, config,
					android.BazelLabelForModuleDeps(ctx, baseProps.Libs))
				data := android.BazelLabelForModuleSrc(ctx, baseProps.Data)
				data.Append(android.BazelLabelForModuleSrc(ctx, baseProps.Java_data))
				attrs.Data.SetSelectValue(axis, config, data)
			}
		}
	}

	partitionedSrcs := bazel.PartitionLabelListAttribute(ctx, &attrs.Srcs, bazel.LabelPartitions{
		"proto": android.ProtoSrcLabelPartition,
		"py":    bazel.LabelPartition{Keep_remainder: true},
	})
	attrs.Srcs = partitionedSrcs["py"]

	if !partitionedSrcs["proto"].IsEmpty() {
		protoInfo, _ := android.Bp2buildProtoProperties(ctx, &m.ModuleBase, partitionedSrcs["proto"])
		protoLabel := bazel.Label{Label: ":" + protoInfo.Name}

		pyProtoLibraryName := m.Name() + "_py_proto"
		ctx.CreateBazelTargetModule(bazel.BazelTargetModuleProperties{
			Rule_class:        "py_proto_library",
			Bzl_load_location: "//build/bazel/rules/python:py_proto.bzl",
		}, android.CommonAttributes{
			Name: pyProtoLibraryName,
		}, &bazelPythonProtoLibraryAttributes{
			Deps: bazel.MakeSingleLabelListAttribute(protoLabel),
		})

		attrs.Deps.Add(bazel.MakeLabelAttribute(":" + pyProtoLibraryName))
	}

	// Bazel normally requires `import path.from.top.of.tree` statements in
	// python code, but with soong you can directly import modules from libraries.
	// Add "imports" attributes to the bazel library so it matches soong's behavior.
	imports := "."
	if m.properties.Pkg_path != nil {
		// TODO(b/215119317) This is a hack to handle the fact that we don't convert
		// pkg_path properly right now. If the folder structure that contains this
		// Android.bp file matches pkg_path, we can set imports to an appropriate
		// number of ../..s to emulate moving the files under a pkg_path folder.
		pkg_path := filepath.Clean(*m.properties.Pkg_path)
		if strings.HasPrefix(pkg_path, "/") {
			ctx.ModuleErrorf("pkg_path cannot start with a /: %s", pkg_path)
		}

		if !strings.HasSuffix(ctx.ModuleDir(), "/"+pkg_path) && ctx.ModuleDir() != pkg_path {
			ctx.ModuleErrorf("Currently, bp2build only supports pkg_paths that are the same as the folders the Android.bp file is in. pkg_path: %s, module directory: %s", pkg_path, ctx.ModuleDir())
		}
		numFolders := strings.Count(pkg_path, "/") + 1
		dots := make([]string, numFolders)
		for i := 0; i < numFolders; i++ {
			dots[i] = ".."
		}
		imports = strings.Join(dots, "/")
	}
	attrs.Imports = bazel.MakeStringListAttribute([]string{imports})

	return attrs
}

func pythonLibBp2Build(ctx android.TopDownMutatorContext, m *PythonLibraryModule) {
	// TODO(b/182306917): this doesn't fully handle all nested props versioned
	// by the python version, which would have been handled by the version split
	// mutator. This is sufficient for very simple python_library modules under
	// Bionic.
	py3Enabled := proptools.BoolDefault(m.properties.Version.Py3.Enabled, true)
	py2Enabled := proptools.BoolDefault(m.properties.Version.Py2.Enabled, false)
	var python_version *string
	if py2Enabled && !py3Enabled {
		python_version = &pyVersion2
	} else if !py2Enabled && py3Enabled {
		python_version = &pyVersion3
	} else if !py2Enabled && !py3Enabled {
		ctx.ModuleErrorf("bp2build converter doesn't understand having neither py2 nor py3 enabled")
	} else {
		// do nothing, since python_version defaults to PY2ANDPY3
	}

	baseAttrs := m.makeArchVariantBaseAttributes(ctx)

	attrs := &bazelPythonLibraryAttributes{
		Srcs:         baseAttrs.Srcs,
		Deps:         baseAttrs.Deps,
		Srcs_version: python_version,
		Imports:      baseAttrs.Imports,
	}

	props := bazel.BazelTargetModuleProperties{
		// Use the native py_library rule.
		Rule_class: "py_library",
	}

	ctx.CreateBazelTargetModule(props, android.CommonAttributes{
		Name: m.Name(),
		Data: baseAttrs.Data,
	}, attrs)
}

type bazelPythonBinaryAttributes struct {
	Main           *bazel.Label
	Srcs           bazel.LabelListAttribute
	Deps           bazel.LabelListAttribute
	Python_version *string
	Imports        bazel.StringListAttribute
}

func pythonBinaryBp2Build(ctx android.TopDownMutatorContext, m *PythonBinaryModule) {
	// TODO(b/182306917): this doesn't fully handle all nested props versioned
	// by the python version, which would have been handled by the version split
	// mutator. This is sufficient for very simple python_binary_host modules
	// under Bionic.
	py3Enabled := proptools.BoolDefault(m.properties.Version.Py3.Enabled, false)
	py2Enabled := proptools.BoolDefault(m.properties.Version.Py2.Enabled, false)
	var python_version *string
	if py3Enabled && py2Enabled {
		panic(fmt.Errorf(
			"error for '%s' module: bp2build's python_binary_host converter does not support "+
				"converting a module that is enabled for both Python 2 and 3 at the same time.", m.Name()))
	} else if py2Enabled {
		python_version = &pyVersion2
	} else {
		// do nothing, since python_version defaults to PY3.
	}

	baseAttrs := m.makeArchVariantBaseAttributes(ctx)
	attrs := &bazelPythonBinaryAttributes{
		Main:           nil,
		Srcs:           baseAttrs.Srcs,
		Deps:           baseAttrs.Deps,
		Python_version: python_version,
		Imports:        baseAttrs.Imports,
	}

	for _, propIntf := range m.GetProperties() {
		if props, ok := propIntf.(*BinaryProperties); ok {
			// main is optional.
			if props.Main != nil {
				main := android.BazelLabelForModuleSrcSingle(ctx, *props.Main)
				attrs.Main = &main
				break
			}
		}
	}

	props := bazel.BazelTargetModuleProperties{
		// Use the native py_binary rule.
		Rule_class: "py_binary",
	}

	ctx.CreateBazelTargetModule(props, android.CommonAttributes{
		Name: m.Name(),
		Data: baseAttrs.Data,
	}, attrs)
}

func (p *PythonLibraryModule) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
	pythonLibBp2Build(ctx, p)
}

func (p *PythonBinaryModule) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
	pythonBinaryBp2Build(ctx, p)
}

func (p *PythonTestModule) ConvertWithBp2build(_ android.TopDownMutatorContext) {
	// Tests are currently unsupported
}
Loading