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

Commit 2b8004b1 authored by Liz Kammer's avatar Liz Kammer
Browse files

bp2build: Add support for cc_binary.

Bug: 197920036
Test: build/bazel/ci/bp2build.sh
Change-Id: I2c4200967653af15a330ab8cbaf796b70d43f32d
parent ae3994ed
Loading
Loading
Loading
Loading
+18 −0
Original line number Diff line number Diff line
@@ -253,6 +253,10 @@ var (
		"cap_names.h", // http://b/198596102, depends on _makenames, a cc_binary

		"libminijail", // depends on unconverted modules: libcap
		"getcap",      // depends on unconverted modules: libcap
		"setcap",      // depends on unconverted modules: libcap
		"minijail0",   // depends on unconverted modules: libcap, libminijail
		"drop_privs",  // depends on unconverted modules: libminijail

		// Tests. Handle later.
		"libbionic_tests_headers_posix", // http://b/186024507, cc_library_static, sched.h, time.h not found
@@ -279,6 +283,20 @@ var (

		"libgtest_ndk_c++",      // b/201816222: Requires sdk_version support.
		"libgtest_main_ndk_c++", // b/201816222: Requires sdk_version support.

		"abb",                     // depends on unconverted modules: libadbd_core, libadbd_services, libcmd, libbinder, libutils, libselinux
		"adb",                     // depends on unconverted modules: bin2c_fastdeployagent, libadb_crypto, libadb_host, libadb_pairing_connection, libadb_protos, libandroidfw, libapp_processes_protos_full, libfastdeploy_host, libmdnssd, libopenscreen-discovery, libopenscreen-platform-impl, libusb, libutils, libziparchive, libzstd, AdbWinApi
		"adbd",                    // depends on unconverted modules: libadb_crypto, libadb_pairing_connection, libadb_protos, libadbd, libadbd_core, libapp_processes_protos_lite, libmdnssd, libzstd, libadbd_services, libcap, libminijail, libselinux
		"bionic_tests_zipalign",   // depends on unconverted modules: libziparchive, libutils
		"linker",                  // depends on unconverted modules: liblinker_debuggerd_stub, libdebuggerd_handler_fallback, libziparchive, liblinker_main, liblinker_malloc
		"linker_reloc_bench_main", // depends on unconverted modules: liblinker_reloc_bench_*
		"sefcontext_compile",      // depends on unconverted modules: libsepol
		"versioner",               // depends on unconverted modules: libclang_cxx_host, libLLVM_host

		"linkerconfig", // http://b/202876379 has arch-variant static_executable
		"mdnsd",        // http://b/202876379 has arch-variant static_executable

		"acvp_modulewrapper", // disabled for android x86/x86_64
	}

	// Per-module denylist of cc_library modules to only generate the static
+415 −0
Original line number Diff line number Diff line
// Copyright 2021 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/cc"
	"fmt"
	"strings"
	"testing"
)

const (
	ccBinaryTypePlaceHolder   = "{rule_name}"
	compatibleWithPlaceHolder = "{target_compatible_with}"
)

func registerCcBinaryModuleTypes(ctx android.RegistrationContext) {
	cc.RegisterCCBuildComponents(ctx)
	ctx.RegisterModuleType("filegroup", android.FileGroupFactory)
	ctx.RegisterModuleType("cc_library_static", cc.LibraryStaticFactory)
	ctx.RegisterModuleType("cc_library", cc.LibraryFactory)
}

var binaryReplacer = strings.NewReplacer(ccBinaryTypePlaceHolder, "cc_binary", compatibleWithPlaceHolder, "")
var hostBinaryReplacer = strings.NewReplacer(ccBinaryTypePlaceHolder, "cc_binary_host", compatibleWithPlaceHolder, `
    target_compatible_with = select({
        "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
        "//conditions:default": [],
    }),`)

func runCcBinaryTests(t *testing.T, tc bp2buildTestCase) {
	t.Helper()
	runCcBinaryTestCase(t, tc)
	runCcHostBinaryTestCase(t, tc)
}

func runCcBinaryTestCase(t *testing.T, tc bp2buildTestCase) {
	t.Helper()
	testCase := tc
	testCase.expectedBazelTargets = append([]string{}, tc.expectedBazelTargets...)
	testCase.moduleTypeUnderTest = "cc_binary"
	testCase.moduleTypeUnderTestFactory = cc.BinaryFactory
	testCase.moduleTypeUnderTestBp2BuildMutator = cc.BinaryBp2build
	testCase.description = fmt.Sprintf("%s %s", testCase.moduleTypeUnderTest, testCase.description)
	testCase.blueprint = binaryReplacer.Replace(testCase.blueprint)
	for i, et := range testCase.expectedBazelTargets {
		testCase.expectedBazelTargets[i] = binaryReplacer.Replace(et)
	}
	t.Run(testCase.description, func(t *testing.T) {
		runBp2BuildTestCase(t, registerCcBinaryModuleTypes, testCase)
	})
}

func runCcHostBinaryTestCase(t *testing.T, tc bp2buildTestCase) {
	t.Helper()
	testCase := tc
	testCase.expectedBazelTargets = append([]string{}, tc.expectedBazelTargets...)
	testCase.moduleTypeUnderTest = "cc_binary_host"
	testCase.moduleTypeUnderTestFactory = cc.BinaryHostFactory
	testCase.moduleTypeUnderTestBp2BuildMutator = cc.BinaryHostBp2build
	testCase.description = fmt.Sprintf("%s %s", testCase.moduleTypeUnderTest, testCase.description)
	testCase.blueprint = hostBinaryReplacer.Replace(testCase.blueprint)
	for i, et := range testCase.expectedBazelTargets {
		testCase.expectedBazelTargets[i] = hostBinaryReplacer.Replace(et)
	}
	t.Run(testCase.description, func(t *testing.T) {
		runBp2BuildTestCase(t, registerCcBinaryModuleTypes, testCase)
	})
}

func TestBasicCcBinary(t *testing.T) {
	runCcBinaryTests(t, bp2buildTestCase{
		description: "basic -- properties -> attrs with little/no transformation",
		blueprint: `
{rule_name} {
    name: "foo",
    srcs: ["a.cc"],
    local_include_dirs: ["dir"],
    include_dirs: ["absolute_dir"],
    cflags: ["-Dcopt"],
    cppflags: ["-Dcppflag"],
    conlyflags: ["-Dconlyflag"],
    asflags: ["-Dasflag"],
    ldflags: ["ld-flag"],
    rtti: true,
    strip: {
        all: true,
        keep_symbols: true,
        keep_symbols_and_debug_frame: true,
        keep_symbols_list: ["symbol"],
        none: true,
    },
}
`,
		expectedBazelTargets: []string{`cc_binary(
    name = "foo",
    absolute_includes = ["absolute_dir"],
    asflags = ["-Dasflag"],
    conlyflags = ["-Dconlyflag"],
    copts = ["-Dcopt"],
    cppflags = ["-Dcppflag"],
    linkopts = ["ld-flag"],
    local_includes = [
        "dir",
        ".",
    ],
    rtti = True,
    srcs = ["a.cc"],
    strip = {
        "all": True,
        "keep_symbols": True,
        "keep_symbols_and_debug_frame": True,
        "keep_symbols_list": ["symbol"],
        "none": True,
    },{target_compatible_with}
)`},
	})
}

func TestCcBinaryWithSharedLdflagDisableFeature(t *testing.T) {
	runCcBinaryTests(t, bp2buildTestCase{
		description: `ldflag "-shared" disables static_flag feature`,
		blueprint: `
{rule_name} {
    name: "foo",
    ldflags: ["-shared"],
    include_build_directory: false,
}
`,
		expectedBazelTargets: []string{`cc_binary(
    name = "foo",
    features = ["-static_flag"],
    linkopts = ["-shared"],{target_compatible_with}
)`},
	})
}

func TestCcBinaryWithLinkStatic(t *testing.T) {
	runCcBinaryTests(t, bp2buildTestCase{
		description: "link static",
		blueprint: `
{rule_name} {
    name: "foo",
    static_executable: true,
    include_build_directory: false,
}
`,
		expectedBazelTargets: []string{`cc_binary(
    name = "foo",
    linkshared = False,{target_compatible_with}
)`},
	})
}

func TestCcBinaryVersionScript(t *testing.T) {
	runCcBinaryTests(t, bp2buildTestCase{
		description: `version script`,
		blueprint: `
{rule_name} {
    name: "foo",
    include_build_directory: false,
    version_script: "vs",
}
`,
		expectedBazelTargets: []string{`cc_binary(
    name = "foo",
    additional_linker_inputs = ["vs"],
    linkopts = ["-Wl,--version-script,$(location vs)"],{target_compatible_with}
)`},
	})
}

func TestCcBinarySplitSrcsByLang(t *testing.T) {
	runCcHostBinaryTestCase(t, bp2buildTestCase{
		description: "split srcs by lang",
		blueprint: `
{rule_name} {
    name: "foo",
    srcs: [
        "asonly.S",
        "conly.c",
        "cpponly.cpp",
        ":fg_foo",
    ],
    include_build_directory: false,
}
` + simpleModuleDoNotConvertBp2build("filegroup", "fg_foo"),
		expectedBazelTargets: []string{`cc_binary(
    name = "foo",
    srcs = [
        "cpponly.cpp",
        ":fg_foo_cpp_srcs",
    ],
    srcs_as = [
        "asonly.S",
        ":fg_foo_as_srcs",
    ],
    srcs_c = [
        "conly.c",
        ":fg_foo_c_srcs",
    ],{target_compatible_with}
)`},
	})
}

func TestCcBinaryDoNotDistinguishBetweenDepsAndImplementationDeps(t *testing.T) {
	runCcBinaryTestCase(t, bp2buildTestCase{
		description: "no implementation deps",
		blueprint: `
{rule_name} {
    name: "foo",
    shared_libs: ["implementation_shared_dep", "shared_dep"],
    export_shared_lib_headers: ["shared_dep"],
    static_libs: ["implementation_static_dep", "static_dep"],
    export_static_lib_headers: ["static_dep", "whole_static_dep"],
    whole_static_libs: ["not_explicitly_exported_whole_static_dep", "whole_static_dep"],
    include_build_directory: false,
}
` +
			simpleModuleDoNotConvertBp2build("cc_library_static", "static_dep") +
			simpleModuleDoNotConvertBp2build("cc_library_static", "implementation_static_dep") +
			simpleModuleDoNotConvertBp2build("cc_library_static", "whole_static_dep") +
			simpleModuleDoNotConvertBp2build("cc_library_static", "not_explicitly_exported_whole_static_dep") +
			simpleModuleDoNotConvertBp2build("cc_library", "shared_dep") +
			simpleModuleDoNotConvertBp2build("cc_library", "implementation_shared_dep"),
		expectedBazelTargets: []string{`cc_binary(
    name = "foo",
    deps = [
        ":implementation_static_dep",
        ":static_dep",
    ],
    dynamic_deps = [
        ":implementation_shared_dep",
        ":shared_dep",
    ],{target_compatible_with}
    whole_archive_deps = [
        ":not_explicitly_exported_whole_static_dep",
        ":whole_static_dep",
    ],
)`},
	})
}

func TestCcBinaryNocrtTests(t *testing.T) {
	baseTestCases := []struct {
		description   string
		soongProperty string
		bazelAttr     string
	}{
		{
			description:   "nocrt: true",
			soongProperty: `nocrt: true,`,
			bazelAttr:     `    link_crt = False,`,
		},
		{
			description:   "nocrt: false",
			soongProperty: `nocrt: false,`,
		},
		{
			description: "nocrt: not set",
		},
	}

	baseBlueprint := `{rule_name} {
    name: "foo",%s
    include_build_directory: false,
}
`

	baseBazelTarget := `cc_binary(
    name = "foo",%s{target_compatible_with}
)`

	for _, btc := range baseTestCases {
		prop := btc.soongProperty
		if len(prop) > 0 {
			prop = "\n" + prop
		}
		attr := btc.bazelAttr
		if len(attr) > 0 {
			attr = "\n" + attr
		}
		runCcBinaryTests(t, bp2buildTestCase{
			description: btc.description,
			blueprint:   fmt.Sprintf(baseBlueprint, prop),
			expectedBazelTargets: []string{
				fmt.Sprintf(baseBazelTarget, attr),
			},
		})
	}
}

func TestCcBinaryNo_libcrtTests(t *testing.T) {
	baseTestCases := []struct {
		description   string
		soongProperty string
		bazelAttr     string
	}{
		{
			description:   "no_libcrt: true",
			soongProperty: `no_libcrt: true,`,
			bazelAttr:     `    use_libcrt = False,`,
		},
		{
			description:   "no_libcrt: false",
			soongProperty: `no_libcrt: false,`,
			bazelAttr:     `    use_libcrt = True,`,
		},
		{
			description: "no_libcrt: not set",
		},
	}

	baseBlueprint := `{rule_name} {
    name: "foo",%s
    include_build_directory: false,
}
`

	baseBazelTarget := `cc_binary(
    name = "foo",{target_compatible_with}%s
)`

	for _, btc := range baseTestCases {
		prop := btc.soongProperty
		if len(prop) > 0 {
			prop = "\n" + prop
		}
		attr := btc.bazelAttr
		if len(attr) > 0 {
			attr = "\n" + attr
		}
		runCcBinaryTests(t, bp2buildTestCase{
			description: btc.description,
			blueprint:   fmt.Sprintf(baseBlueprint, prop),
			expectedBazelTargets: []string{
				fmt.Sprintf(baseBazelTarget, attr),
			},
		})
	}
}

func TestCcBinaryPropertiesToFeatures(t *testing.T) {
	baseTestCases := []struct {
		description   string
		soongProperty string
		bazelAttr     string
	}{
		{
			description:   "pack_relocation: true",
			soongProperty: `pack_relocations: true,`,
		},
		{
			description:   "pack_relocations: false",
			soongProperty: `pack_relocations: false,`,
			bazelAttr:     `    features = ["disable_pack_relocations"],`,
		},
		{
			description: "pack_relocations: not set",
		},
		{
			description:   "pack_relocation: true",
			soongProperty: `allow_undefined_symbols: true,`,
			bazelAttr:     `    features = ["-no_undefined_symbols"],`,
		},
		{
			description:   "allow_undefined_symbols: false",
			soongProperty: `allow_undefined_symbols: false,`,
		},
		{
			description: "allow_undefined_symbols: not set",
		},
	}

	baseBlueprint := `{rule_name} {
    name: "foo",%s
    include_build_directory: false,
}
`

	baseBazelTarget := `cc_binary(
    name = "foo",%s{target_compatible_with}
)`

	for _, btc := range baseTestCases {
		prop := btc.soongProperty
		if len(prop) > 0 {
			prop = "\n" + prop
		}
		attr := btc.bazelAttr
		if len(attr) > 0 {
			attr = "\n" + attr
		}
		runCcBinaryTests(t, bp2buildTestCase{
			description: btc.description,
			blueprint:   fmt.Sprintf(baseBlueprint, prop),
			expectedBazelTargets: []string{
				fmt.Sprintf(baseBazelTarget, attr),
			},
		})
	}
}
+125 −2
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@ import (
	"github.com/google/blueprint"

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

type BinaryLinkerProperties struct {
@@ -62,7 +63,7 @@ func init() {

func RegisterBinaryBuildComponents(ctx android.RegistrationContext) {
	ctx.RegisterModuleType("cc_binary", BinaryFactory)
	ctx.RegisterModuleType("cc_binary_host", binaryHostFactory)
	ctx.RegisterModuleType("cc_binary_host", BinaryHostFactory)
}

// cc_binary produces a binary that is runnable on a device.
@@ -72,7 +73,7 @@ func BinaryFactory() android.Module {
}

// cc_binary_host produces a binary that is runnable on a host.
func binaryHostFactory() android.Module {
func BinaryHostFactory() android.Module {
	module, _ := NewBinary(android.HostSupported)
	return module.Init()
}
@@ -541,3 +542,125 @@ func (binary *binaryDecorator) verifyHostBionicLinker(ctx ModuleContext, in, lin
		},
	})
}

func init() {
	android.RegisterBp2BuildMutator("cc_binary", BinaryBp2build)
	android.RegisterBp2BuildMutator("cc_binary_host", BinaryHostBp2build)
}

func BinaryBp2build(ctx android.TopDownMutatorContext) {
	binaryBp2build(ctx, "cc_binary")
}

func BinaryHostBp2build(ctx android.TopDownMutatorContext) {
	binaryBp2build(ctx, "cc_binary_host")
}

func binaryBp2build(ctx android.TopDownMutatorContext, typ string) {
	m, ok := ctx.Module().(*Module)
	if !ok {
		// Not a cc module
		return
	}
	if !m.ConvertWithBp2build(ctx) {
		return
	}

	if ctx.ModuleType() != typ {
		return
	}

	var compatibleWith bazel.StringListAttribute
	if typ == "cc_binary_host" {
		//incompatible with android OS
		compatibleWith.SetSelectValue(bazel.OsConfigurationAxis, android.Android.Name, []string{"@platforms//:incompatible"})
		compatibleWith.SetSelectValue(bazel.OsConfigurationAxis, bazel.ConditionsDefaultConfigKey, []string{})
	}

	compilerAttrs := bp2BuildParseCompilerProps(ctx, m)
	linkerAttrs := bp2BuildParseLinkerProps(ctx, m)

	attrs := &binaryAttributes{
		binaryLinkerAttrs: bp2buildBinaryLinkerProps(ctx, m),

		Srcs:    compilerAttrs.srcs,
		Srcs_c:  compilerAttrs.cSrcs,
		Srcs_as: compilerAttrs.asSrcs,

		Copts:      compilerAttrs.copts,
		Cppflags:   compilerAttrs.cppFlags,
		Conlyflags: compilerAttrs.conlyFlags,
		Asflags:    compilerAttrs.asFlags,

		Deps:               linkerAttrs.implementationDeps,
		Dynamic_deps:       linkerAttrs.implementationDynamicDeps,
		Whole_archive_deps: linkerAttrs.wholeArchiveDeps,
		System_deps:        linkerAttrs.systemDynamicDeps,

		Local_includes:    compilerAttrs.localIncludes,
		Absolute_includes: compilerAttrs.absoluteIncludes,
		Linkopts:          linkerAttrs.linkopts,
		Link_crt:          linkerAttrs.linkCrt,
		Use_libcrt:        linkerAttrs.useLibcrt,
		Rtti:              compilerAttrs.rtti,
		Stl:               compilerAttrs.stl,
		Cpp_std:           compilerAttrs.cppStd,

		Additional_linker_inputs: linkerAttrs.additionalLinkerInputs,

		Strip: stripAttributes{
			Keep_symbols:                 linkerAttrs.stripKeepSymbols,
			Keep_symbols_and_debug_frame: linkerAttrs.stripKeepSymbolsAndDebugFrame,
			Keep_symbols_list:            linkerAttrs.stripKeepSymbolsList,
			All:                          linkerAttrs.stripAll,
			None:                         linkerAttrs.stripNone,
		},

		Target_compatible_with: compatibleWith,
		Features:               linkerAttrs.features,
	}

	ctx.CreateBazelTargetModule(bazel.BazelTargetModuleProperties{
		Rule_class:        "cc_binary",
		Bzl_load_location: "//build/bazel/rules:cc_binary.bzl",
	},
		android.CommonAttributes{Name: m.Name()},
		attrs)
}

// binaryAttributes contains Bazel attributes corresponding to a cc binary
type binaryAttributes struct {
	binaryLinkerAttrs
	Srcs    bazel.LabelListAttribute
	Srcs_c  bazel.LabelListAttribute
	Srcs_as bazel.LabelListAttribute

	Copts      bazel.StringListAttribute
	Cppflags   bazel.StringListAttribute
	Conlyflags bazel.StringListAttribute
	Asflags    bazel.StringListAttribute

	Deps               bazel.LabelListAttribute
	Dynamic_deps       bazel.LabelListAttribute
	Whole_archive_deps bazel.LabelListAttribute
	System_deps        bazel.LabelListAttribute

	Local_includes    bazel.StringListAttribute
	Absolute_includes bazel.StringListAttribute

	Linkopts                 bazel.StringListAttribute
	Additional_linker_inputs bazel.LabelListAttribute

	Link_crt   bazel.BoolAttribute
	Use_libcrt bazel.BoolAttribute

	Rtti    bazel.BoolAttribute
	Stl     *string
	Cpp_std *string

	Strip stripAttributes

	Features bazel.StringListAttribute

	Target_compatible_with bazel.StringListAttribute
}
+49 −7
Original line number Diff line number Diff line
@@ -117,7 +117,13 @@ type depsPartition struct {

type bazelLabelForDepsFn func(android.TopDownMutatorContext, []string) bazel.LabelList

func partitionExportedAndImplementationsDeps(ctx android.TopDownMutatorContext, allDeps, exportedDeps []string, fn bazelLabelForDepsFn) depsPartition {
func maybePartitionExportedAndImplementationsDeps(ctx android.TopDownMutatorContext, exportsDeps bool, allDeps, exportedDeps []string, fn bazelLabelForDepsFn) depsPartition {
	if !exportsDeps {
		return depsPartition{
			implementation: fn(ctx, allDeps),
		}
	}

	implementation, export := android.FilterList(allDeps, exportedDeps)

	return depsPartition{
@@ -128,7 +134,12 @@ func partitionExportedAndImplementationsDeps(ctx android.TopDownMutatorContext,

type bazelLabelForDepsExcludesFn func(android.TopDownMutatorContext, []string, []string) bazel.LabelList

func partitionExportedAndImplementationsDepsExcludes(ctx android.TopDownMutatorContext, allDeps, excludes, exportedDeps []string, fn bazelLabelForDepsExcludesFn) depsPartition {
func maybePartitionExportedAndImplementationsDepsExcludes(ctx android.TopDownMutatorContext, exportsDeps bool, allDeps, excludes, exportedDeps []string, fn bazelLabelForDepsExcludesFn) depsPartition {
	if !exportsDeps {
		return depsPartition{
			implementation: fn(ctx, allDeps, excludes),
		}
	}
	implementation, export := android.FilterList(allDeps, exportedDeps)

	return depsPartition{
@@ -145,11 +156,11 @@ func bp2buildParseStaticOrSharedProps(ctx android.TopDownMutatorContext, module
		attrs.Srcs.SetSelectValue(axis, config, android.BazelLabelForModuleSrc(ctx, props.Srcs))
		attrs.System_dynamic_deps.SetSelectValue(axis, config, bazelLabelForSharedDeps(ctx, props.System_shared_libs))

		staticDeps := partitionExportedAndImplementationsDeps(ctx, props.Static_libs, props.Export_static_lib_headers, bazelLabelForStaticDeps)
		staticDeps := maybePartitionExportedAndImplementationsDeps(ctx, true, props.Static_libs, props.Export_static_lib_headers, bazelLabelForStaticDeps)
		attrs.Deps.SetSelectValue(axis, config, staticDeps.export)
		attrs.Implementation_deps.SetSelectValue(axis, config, staticDeps.implementation)

		sharedDeps := partitionExportedAndImplementationsDeps(ctx, props.Shared_libs, props.Export_shared_lib_headers, bazelLabelForSharedDeps)
		sharedDeps := maybePartitionExportedAndImplementationsDeps(ctx, true, props.Shared_libs, props.Export_shared_lib_headers, bazelLabelForSharedDeps)
		attrs.Dynamic_deps.SetSelectValue(axis, config, sharedDeps.export)
		attrs.Implementation_dynamic_deps.SetSelectValue(axis, config, sharedDeps.implementation)

@@ -465,6 +476,7 @@ func bp2BuildParseLinkerProps(ctx android.TopDownMutatorContext, module *Module)

	// Use a single variable to capture usage of nocrt in arch variants, so there's only 1 error message for this module
	var disallowedArchVariantCrt bool
	isBinary := module.Binary()

	for axis, configToProps := range module.GetArchVariantProperties(ctx, &BaseLinkerProperties{}) {
		for config, props := range configToProps {
@@ -474,7 +486,7 @@ func bp2BuildParseLinkerProps(ctx android.TopDownMutatorContext, module *Module)
				// Excludes to parallel Soong:
				// https://cs.android.com/android/platform/superproject/+/master:build/soong/cc/linker.go;l=247-249;drc=088b53577dde6e40085ffd737a1ae96ad82fc4b0
				staticLibs := android.FirstUniqueStrings(baseLinkerProps.Static_libs)
				staticDeps := partitionExportedAndImplementationsDepsExcludes(ctx, staticLibs, baseLinkerProps.Exclude_static_libs, baseLinkerProps.Export_static_lib_headers, bazelLabelForStaticDepsExcludes)
				staticDeps := maybePartitionExportedAndImplementationsDepsExcludes(ctx, !isBinary, staticLibs, baseLinkerProps.Exclude_static_libs, baseLinkerProps.Export_static_lib_headers, bazelLabelForStaticDepsExcludes)
				deps.SetSelectValue(axis, config, staticDeps.export)
				implementationDeps.SetSelectValue(axis, config, staticDeps.implementation)

@@ -491,12 +503,12 @@ func bp2BuildParseLinkerProps(ctx android.TopDownMutatorContext, module *Module)
				systemSharedDeps.SetSelectValue(axis, config, bazelLabelForSharedDeps(ctx, systemSharedLibs))

				sharedLibs := android.FirstUniqueStrings(baseLinkerProps.Shared_libs)
				sharedDeps := partitionExportedAndImplementationsDepsExcludes(ctx, sharedLibs, baseLinkerProps.Exclude_shared_libs, baseLinkerProps.Export_shared_lib_headers, bazelLabelForSharedDepsExcludes)
				sharedDeps := maybePartitionExportedAndImplementationsDepsExcludes(ctx, !isBinary, sharedLibs, baseLinkerProps.Exclude_shared_libs, baseLinkerProps.Export_shared_lib_headers, bazelLabelForSharedDepsExcludes)
				dynamicDeps.SetSelectValue(axis, config, sharedDeps.export)
				implementationDynamicDeps.SetSelectValue(axis, config, sharedDeps.implementation)

				headerLibs := android.FirstUniqueStrings(baseLinkerProps.Header_libs)
				hDeps := partitionExportedAndImplementationsDeps(ctx, headerLibs, baseLinkerProps.Export_header_lib_headers, bazelLabelForHeaderDeps)
				hDeps := maybePartitionExportedAndImplementationsDeps(ctx, !isBinary, headerLibs, baseLinkerProps.Export_header_lib_headers, bazelLabelForHeaderDeps)

				headerDeps.SetSelectValue(axis, config, hDeps.export)
				implementationHeaderDeps.SetSelectValue(axis, config, hDeps.implementation)
@@ -512,6 +524,10 @@ func bp2BuildParseLinkerProps(ctx android.TopDownMutatorContext, module *Module)
				var linkerFlags []string
				if len(baseLinkerProps.Ldflags) > 0 {
					linkerFlags = append(linkerFlags, baseLinkerProps.Ldflags...)
					// binaries remove static flag if -shared is in the linker flags
					if module.Binary() && android.InList("-shared", linkerFlags) {
						axisFeatures = append(axisFeatures, "-static_flag")
					}
				}
				if baseLinkerProps.Version_script != nil {
					label := android.BazelLabelForModuleSrcSingle(ctx, *baseLinkerProps.Version_script)
@@ -746,3 +762,29 @@ func bazelLabelForHeaderDeps(ctx android.TopDownMutatorContext, modules []string
func bazelLabelForSharedDepsExcludes(ctx android.TopDownMutatorContext, modules, excludes []string) bazel.LabelList {
	return android.BazelLabelForModuleDepsExcludesWithFn(ctx, modules, excludes, bazelLabelForSharedModule)
}

type binaryLinkerAttrs struct {
	Linkshared *bool
}

func bp2buildBinaryLinkerProps(ctx android.TopDownMutatorContext, m *Module) binaryLinkerAttrs {
	attrs := binaryLinkerAttrs{}
	archVariantProps := m.GetArchVariantProperties(ctx, &BinaryLinkerProperties{})
	for axis, configToProps := range archVariantProps {
		for _, p := range configToProps {
			props := p.(*BinaryLinkerProperties)
			staticExecutable := props.Static_executable
			if axis == bazel.NoConfigAxis {
				if linkBinaryShared := !proptools.Bool(staticExecutable); !linkBinaryShared {
					attrs.Linkshared = &linkBinaryShared
				}
			} else if staticExecutable != nil {
				// TODO(b/202876379): Static_executable is arch-variant; however, linkshared is a
				// nonconfigurable attribute. Only 4 AOSP modules use this feature, defer handling
				ctx.ModuleErrorf("bp2build cannot migrate a module with arch/target-specific static_executable values")
			}
		}
	}

	return attrs
}