Merge "Revert "Export non-apex variants of modules to make""
diff --git a/android/allowlists/allowlists.go b/android/allowlists/allowlists.go
index 63495bc..17db472 100644
--- a/android/allowlists/allowlists.go
+++ b/android/allowlists/allowlists.go
@@ -218,6 +218,7 @@
 
 		"hardware/interfaces":                          Bp2BuildDefaultTrue,
 		"hardware/interfaces/audio/aidl":               Bp2BuildDefaultTrue,
+		"hardware/interfaces/audio/aidl/common":        Bp2BuildDefaultTrue,
 		"hardware/interfaces/common/aidl":              Bp2BuildDefaultTrue,
 		"hardware/interfaces/common/fmq/aidl":          Bp2BuildDefaultTrue,
 		"hardware/interfaces/configstore/1.0":          Bp2BuildDefaultTrue,
diff --git a/android/config.go b/android/config.go
index b37d5c8..979f1ca 100644
--- a/android/config.go
+++ b/android/config.go
@@ -1759,6 +1759,10 @@
 	return c.config.productVariables.BuildBrokenTrebleSyspropNeverallow
 }
 
+func (c *deviceConfig) BuildBrokenUsesSoongPython2Modules() bool {
+	return c.config.productVariables.BuildBrokenUsesSoongPython2Modules
+}
+
 func (c *deviceConfig) BuildDebugfsRestrictionsEnabled() bool {
 	return c.config.productVariables.BuildDebugfsRestrictionsEnabled
 }
@@ -1837,3 +1841,14 @@
 		c.mixedBuildDisabledModules[moduleName] = struct{}{}
 	}
 }
+
+// ApiSurfaces directory returns the source path inside the api_surfaces repo
+// (relative to workspace root).
+func (c *config) ApiSurfacesDir(s ApiSurface, version string) string {
+	return filepath.Join(
+		"build",
+		"bazel",
+		"api_surfaces",
+		s.String(),
+		version)
+}
diff --git a/android/filegroup.go b/android/filegroup.go
index 7d929bc..0f6e00e 100644
--- a/android/filegroup.go
+++ b/android/filegroup.go
@@ -78,6 +78,12 @@
 	Strip_import_prefix *string
 }
 
+// api srcs can be contained in filegroups.
+// this should be generated in api_bp2build workspace as well.
+func (fg *fileGroup) ConvertWithApiBp2build(ctx TopDownMutatorContext) {
+	fg.ConvertWithBp2build(ctx)
+}
+
 // ConvertWithBp2build performs bp2build conversion of filegroup
 func (fg *fileGroup) ConvertWithBp2build(ctx TopDownMutatorContext) {
 	srcs := bazel.MakeLabelListAttribute(
diff --git a/android/mutator.go b/android/mutator.go
index 4dacb8d..676f8a5 100644
--- a/android/mutator.go
+++ b/android/mutator.go
@@ -268,6 +268,11 @@
 	// platforms, as dictated by a given bool attribute: the target will not be buildable in
 	// any platform for which this bool attribute is false.
 	CreateBazelTargetModuleWithRestrictions(bazel.BazelTargetModuleProperties, CommonAttributes, interface{}, bazel.BoolAttribute)
+
+	// CreateBazelTargetAliasInDir creates an alias definition in `dir` directory.
+	// This function can be used to create alias definitions in a directory that is different
+	// from the directory of the visited Soong module.
+	CreateBazelTargetAliasInDir(dir string, name string, actual bazel.Label)
 }
 
 type topDownMutatorContext struct {
@@ -705,6 +710,34 @@
 	t.createBazelTargetModule(bazelProps, commonAttrs, attrs, enabledProperty)
 }
 
+var (
+	bazelAliasModuleProperties = bazel.BazelTargetModuleProperties{
+		Rule_class: "alias",
+	}
+)
+
+type bazelAliasAttributes struct {
+	Actual *bazel.LabelAttribute
+}
+
+func (t *topDownMutatorContext) CreateBazelTargetAliasInDir(
+	dir string,
+	name string,
+	actual bazel.Label) {
+	mod := t.Module()
+	attrs := &bazelAliasAttributes{
+		Actual: bazel.MakeLabelAttribute(actual.Label),
+	}
+	info := bp2buildInfo{
+		Dir:             dir,
+		BazelProps:      bazelAliasModuleProperties,
+		CommonAttrs:     CommonAttributes{Name: name},
+		ConstraintAttrs: constraintAttributes{},
+		Attrs:           attrs,
+	}
+	mod.base().addBp2buildInfo(info)
+}
+
 // ApexAvailableTags converts the apex_available property value of an ApexModule
 // module and returns it as a list of keyed tags.
 func ApexAvailableTags(mod Module) bazel.StringListAttribute {
diff --git a/android/variable.go b/android/variable.go
index 1b5d558..8c5c0bc 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -442,6 +442,7 @@
 	BuildBrokenDepfile                 *bool    `json:",omitempty"`
 	BuildBrokenEnforceSyspropOwner     bool     `json:",omitempty"`
 	BuildBrokenTrebleSyspropNeverallow bool     `json:",omitempty"`
+	BuildBrokenUsesSoongPython2Modules bool     `json:",omitempty"`
 	BuildBrokenVendorPropertyNamespace bool     `json:",omitempty"`
 	BuildBrokenInputDirModules         []string `json:",omitempty"`
 
diff --git a/bp2build/build_conversion.go b/bp2build/build_conversion.go
index ced779c..fde9b69 100644
--- a/bp2build/build_conversion.go
+++ b/bp2build/build_conversion.go
@@ -60,6 +60,15 @@
 	}
 }
 
+// PackageName returns the package of the Bazel target.
+// Defaults to root of tree.
+func (t BazelTarget) PackageName() string {
+	if t.packageName == "" {
+		return "."
+	}
+	return t.packageName
+}
+
 // BazelTargets is a typedef for a slice of BazelTarget objects.
 type BazelTargets []BazelTarget
 
@@ -337,7 +346,10 @@
 			return
 		}
 
-		buildFileToTargets[dir] = append(buildFileToTargets[dir], targets...)
+		for _, target := range targets {
+			targetDir := target.PackageName()
+			buildFileToTargets[targetDir] = append(buildFileToTargets[targetDir], target)
+		}
 	})
 
 	if len(errs) > 0 {
@@ -454,7 +466,8 @@
 
 	targetName := targetNameWithVariant(ctx, m)
 	return BazelTarget{
-		name: targetName,
+		name:        targetName,
+		packageName: ctx.ModuleDir(m),
 		content: fmt.Sprintf(
 			soongModuleTargetTemplate,
 			targetName,
diff --git a/bp2build/java_library_conversion_test.go b/bp2build/java_library_conversion_test.go
index 0784f4b..f1d6398 100644
--- a/bp2build/java_library_conversion_test.go
+++ b/bp2build/java_library_conversion_test.go
@@ -755,3 +755,29 @@
 		},
 	})
 }
+
+func TestJavaLibraryArchVariantSrcsWithExcludes(t *testing.T) {
+	runJavaLibraryTestCase(t, Bp2buildTestCase{
+		Description: "java_library with arch variant libs",
+		Blueprint: `java_library {
+    name: "java-lib-1",
+    srcs: ["a.java", "b.java"],
+    target: {
+        android: {
+            exclude_srcs: ["a.java"],
+        },
+    },
+    bazel_module: { bp2build_available: true },
+}
+`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("java_library", "java-lib-1", AttrNameToString{
+				"srcs": `["b.java"] + select({
+        "//build/bazel/platforms/os:android": [],
+        "//conditions:default": ["a.java"],
+    })`,
+			}),
+			MakeNeverlinkDuplicateTarget("java_library", "java-lib-1"),
+		},
+	})
+}
diff --git a/bp2build/testing.go b/bp2build/testing.go
index bac5383..a737ea1 100644
--- a/bp2build/testing.go
+++ b/bp2build/testing.go
@@ -21,6 +21,7 @@
 
 import (
 	"fmt"
+	"sort"
 	"strings"
 	"testing"
 
@@ -263,6 +264,12 @@
 		t.Errorf("%s: Expected %d bazel target (%s), got %d (%s)",
 			description, expectedCount, expectedContents, actualCount, actualTargets)
 	} else {
+		sort.SliceStable(actualTargets, func(i, j int) bool {
+			return actualTargets[i].name < actualTargets[j].name
+		})
+		sort.SliceStable(expectedContents, func(i, j int) bool {
+			return getTargetName(expectedContents[i]) < getTargetName(expectedContents[j])
+		})
 		for i, actualTarget := range actualTargets {
 			if w, g := expectedContents[i], actualTarget.content; w != g {
 				t.Errorf(
@@ -637,3 +644,13 @@
 		"exports":   `[":` + name + `"]`,
 	})
 }
+
+func getTargetName(targetContent string) string {
+	data := strings.Split(targetContent, "name = \"")
+	if len(data) < 2 {
+		return ""
+	} else {
+		endIndex := strings.Index(data[1], "\"")
+		return data[1][:endIndex]
+	}
+}
diff --git a/cc/config/global.go b/cc/config/global.go
index 05dc773..488af45 100644
--- a/cc/config/global.go
+++ b/cc/config/global.go
@@ -193,6 +193,7 @@
 
 	noOverrideGlobalCflags = []string{
 		"-Werror=bool-operation",
+		"-Werror=format-insufficient-args",
 		"-Werror=implicit-int-float-conversion",
 		"-Werror=int-in-bool-context",
 		"-Werror=int-to-pointer-cast",
@@ -247,6 +248,8 @@
 	noOverride64GlobalCflags = []string{}
 
 	noOverrideExternalGlobalCflags = []string{
+		// http://b/191699019
+		"-Wno-format-insufficient-args",
 		"-Wno-sizeof-array-div",
 		"-Wno-unused-but-set-variable",
 		"-Wno-unused-but-set-parameter",
@@ -284,9 +287,6 @@
 
 		// http://b/239661264
 		"-Wno-deprecated-non-prototype",
-
-		// http://b/191699019
-		"-Wno-format-insufficient-args",
 	}
 
 	llvmNextExtraCommonGlobalCflags = []string{
diff --git a/cc/library.go b/cc/library.go
index e73af81..27f0623 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -464,6 +464,21 @@
 		ctx.CreateBazelTargetModule(stubSuitesProps,
 			android.CommonAttributes{Name: m.Name() + "_stub_libs"},
 			stubSuitesAttrs)
+
+		// Add alias for the stub shared_library in @api_surfaces repository
+		currentModuleLibApiDir := ctx.Config().ApiSurfacesDir(android.ModuleLibApi, "current")
+		actualLabelInMainWorkspace := bazel.Label{
+			Label: fmt.Sprintf("@//%s:%s_stub_libs_current", ctx.ModuleDir(), m.Name()),
+		}
+		ctx.CreateBazelTargetAliasInDir(currentModuleLibApiDir, m.Name(), actualLabelInMainWorkspace)
+
+		// Add alias for headers exported by the stub library
+		headerLabelInMainWorkspace := bazel.Label{
+			// This label is generated from cc_stub_suite macro
+			Label: fmt.Sprintf("@//%s:%s_stub_libs_%s_headers", ctx.ModuleDir(), m.Name(), android.ModuleLibApi.String()),
+		}
+		headerAlias := m.Name() + "_headers"
+		ctx.CreateBazelTargetAliasInDir(currentModuleLibApiDir, headerAlias, headerLabelInMainWorkspace)
 	}
 }
 
diff --git a/cmd/soong_build/main.go b/cmd/soong_build/main.go
index 5c187f6..9b51160 100644
--- a/cmd/soong_build/main.go
+++ b/cmd/soong_build/main.go
@@ -136,7 +136,7 @@
 	ctx.EventHandler.Begin("queryview")
 	defer ctx.EventHandler.End("queryview")
 	codegenContext := bp2build.NewCodegenContext(ctx.Config(), ctx, bp2build.QueryView, topDir)
-	err := createBazelWorkspace(codegenContext, shared.JoinPath(topDir, queryviewDir))
+	err := createBazelWorkspace(codegenContext, shared.JoinPath(topDir, queryviewDir), false)
 	maybeQuit(err, "")
 	touch(shared.JoinPath(topDir, queryviewMarker))
 }
@@ -174,7 +174,28 @@
 	// Run codegen to generate BUILD files
 	codegenContext := bp2build.NewCodegenContext(ctx.Config(), ctx, bp2build.ApiBp2build, topDir)
 	absoluteApiBp2buildDir := shared.JoinPath(topDir, cmdlineArgs.BazelApiBp2buildDir)
-	err := createBazelWorkspace(codegenContext, absoluteApiBp2buildDir)
+	// Always generate bp2build_all_srcs filegroups in api_bp2build.
+	// This is necessary to force each Android.bp file to create an equivalent BUILD file
+	// and prevent package boundray issues.
+	// e.g.
+	// Source
+	// f/b/Android.bp
+	// java_library{
+	//   name: "foo",
+	//   api: "api/current.txt",
+	// }
+	//
+	// f/b/api/Android.bp <- will cause package boundary issues
+	//
+	// Gen
+	// f/b/BUILD
+	// java_contribution{
+	//   name: "foo.contribution",
+	//   api: "//f/b/api:current.txt",
+	// }
+	//
+	// If we don't generate f/b/api/BUILD, foo.contribution will be unbuildable.
+	err := createBazelWorkspace(codegenContext, absoluteApiBp2buildDir, true)
 	maybeQuit(err, "")
 	ninjaDeps = append(ninjaDeps, codegenContext.AdditionalNinjaDeps()...)
 
diff --git a/cmd/soong_build/queryview.go b/cmd/soong_build/queryview.go
index 35ae009..ce32184 100644
--- a/cmd/soong_build/queryview.go
+++ b/cmd/soong_build/queryview.go
@@ -25,11 +25,11 @@
 )
 
 // A helper function to generate a Read-only Bazel workspace in outDir
-func createBazelWorkspace(ctx *bp2build.CodegenContext, outDir string) error {
+func createBazelWorkspace(ctx *bp2build.CodegenContext, outDir string, generateFilegroups bool) error {
 	os.RemoveAll(outDir)
 	ruleShims := bp2build.CreateRuleShims(android.ModuleTypeFactories())
 
-	res, err := bp2build.GenerateBazelTargets(ctx, false)
+	res, err := bp2build.GenerateBazelTargets(ctx, generateFilegroups)
 	if err != nil {
 		panic(err)
 	}
diff --git a/java/java.go b/java/java.go
index 63c4416..61f5949 100644
--- a/java/java.go
+++ b/java/java.go
@@ -2660,6 +2660,7 @@
 			}
 		}
 	}
+	srcs.ResolveExcludes()
 
 	javaSrcPartition := "java"
 	protoSrcPartition := "proto"
diff --git a/python/python.go b/python/python.go
index 0ae7b36..c7c523d 100644
--- a/python/python.go
+++ b/python/python.go
@@ -263,6 +263,12 @@
 				versionProps = append(versionProps, props.Version.Py3)
 			}
 			if proptools.BoolDefault(props.Version.Py2.Enabled, false) {
+				if !mctx.DeviceConfig().BuildBrokenUsesSoongPython2Modules() &&
+					mctx.ModuleName() != "par_test" &&
+					mctx.ModuleName() != "py2-cmd" &&
+					mctx.ModuleName() != "py2-stdlib" {
+					mctx.PropertyErrorf("version.py2.enabled", "Python 2 is no longer supported, please convert to python 3. This error can be temporarily overridden by setting BUILD_BROKEN_USES_SOONG_PYTHON2_MODULES := true in the product configuration")
+				}
 				versionNames = append(versionNames, pyVersion2)
 				versionProps = append(versionProps, props.Version.Py2)
 			}
diff --git a/scripts/Android.bp b/scripts/Android.bp
index 5dd45cd..ddbba74 100644
--- a/scripts/Android.bp
+++ b/scripts/Android.bp
@@ -191,6 +191,17 @@
     ],
 }
 
+python_test_host {
+    name: "conv_linker_config_test",
+    main: "conv_linker_config_test.py",
+    srcs: [
+        "conv_linker_config_test.py",
+        "conv_linker_config.py",
+    ],
+    libs: ["linker_config_proto"],
+    test_suites: ["general-tests"],
+}
+
 python_binary_host {
     name: "get_clang_version",
     main: "get_clang_version.py",
diff --git a/scripts/conv_linker_config.py b/scripts/conv_linker_config.py
index 784a92f..3ac1b7e 100644
--- a/scripts/conv_linker_config.py
+++ b/scripts/conv_linker_config.py
@@ -27,6 +27,19 @@
 from google.protobuf.text_format import MessageToString
 
 
+def LoadJsonMessage(path):
+    """
+    Loads a message from a .json file with `//` comments strippedfor convenience.
+    """
+    json_content = ''
+    with open(path) as f:
+        for line in f:
+            if not line.lstrip().startswith('//'):
+                json_content += line
+    obj = json.loads(json_content, object_pairs_hook=collections.OrderedDict)
+    return ParseDict(obj, linker_config_pb2.LinkerConfig())
+
+
 def Proto(args):
     """
     Merges input json files (--source) into a protobuf message (--output).
@@ -48,13 +61,7 @@
 
     if args.source:
         for input in args.source.split(':'):
-            json_content = ''
-            with open(input) as f:
-                for line in f:
-                    if not line.lstrip().startswith('//'):
-                        json_content += line
-            obj = json.loads(json_content, object_pairs_hook=collections.OrderedDict)
-            ParseDict(obj, pb)
+            pb.MergeFrom(LoadJsonMessage(input))
     with open(args.output, 'wb') as f:
         f.write(pb.SerializeToString())
 
diff --git a/scripts/conv_linker_config_test.py b/scripts/conv_linker_config_test.py
new file mode 100644
index 0000000..d19a47b
--- /dev/null
+++ b/scripts/conv_linker_config_test.py
@@ -0,0 +1,132 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2023 The Android Open Source Project
+#
+# 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.
+#
+"""Unit tests for conv_linker_config.py."""
+
+import io
+import os
+import shutil
+import tempfile
+import unittest
+
+import conv_linker_config
+from contextlib import redirect_stderr
+from linker_config_pb2 import LinkerConfig
+
+class FileArgs:
+  def __init__(self, files, sep = ':'):
+    self.files = files
+    self.sep = sep
+
+
+class FileArg:
+  def __init__(self, file):
+    self.file = file
+
+
+class TempDirTest(unittest.TestCase):
+
+  def setUp(self):
+    self.tempdir = tempfile.mkdtemp()
+
+
+  def tearDown(self):
+    shutil.rmtree(self.tempdir)
+
+
+  def write(self, name, contents):
+    with open(os.path.join(self.tempdir, name), 'wb') as f:
+      f.write(contents)
+
+
+  def read(self, name):
+    with open(os.path.join(self.tempdir, name), 'rb') as f:
+      return f.read()
+
+
+  def resolve_paths(self, args):
+    for i in range(len(args)):
+      if isinstance(args[i], FileArgs):
+        args[i] = args[i].sep.join(os.path.join(self.tempdir, f.file) for f in args[i].files)
+      elif isinstance(args[i], FileArg):
+        args[i] = os.path.join(self.tempdir, args[i].file)
+    return args
+
+
+class ConvLinkerConfigTest(TempDirTest):
+  """Unit tests for conv_linker_config."""
+
+
+  def test_Proto_empty_input(self):
+    self.command(['proto', '-s', '-o', FileArg('out.pb')])
+    pb = LinkerConfig()
+    pb.ParseFromString(self.read('out.pb'))
+    self.assertEqual(pb, LinkerConfig())
+
+
+  def test_Proto_single_input(self):
+    self.write('foo.json', b'{ "provideLibs": ["libfoo.so"]}')
+    self.command(['proto', '-s', FileArg('foo.json'), '-o', FileArg('out.pb')])
+    pb = LinkerConfig()
+    pb.ParseFromString(self.read('out.pb'))
+    self.assertSequenceEqual(pb.provideLibs, ['libfoo.so'])
+
+
+  def test_Proto_with_multiple_input(self):
+    self.write('foo.json', b'{ "provideLibs": ["libfoo.so"]}')
+    self.write('bar.json', b'{ "provideLibs": ["libbar.so"]}')
+    self.command(['proto', '-s', FileArgs([FileArg('foo.json'), FileArg('bar.json')]), '-o', FileArg('out.pb')])
+    pb = LinkerConfig()
+    pb.ParseFromString(self.read('out.pb'))
+    self.assertSetEqual(set(pb.provideLibs), set(['libfoo.so', 'libbar.so']))
+
+
+  def test_Proto_with_existing_output(self):
+    self.write('out.pb', LinkerConfig(provideLibs=['libfoo.so']).SerializeToString())
+    buf = io.StringIO()
+    with self.assertRaises(SystemExit) as err:
+      with redirect_stderr(buf):
+        self.command(['proto', '-o', FileArg('out.pb')])
+    self.assertEqual(err.exception.code, 1)
+    self.assertRegex(buf.getvalue(), r'.*out\.pb exists')
+
+
+  def test_Proto_with_append(self):
+    self.write('out.pb', LinkerConfig(provideLibs=['libfoo.so']).SerializeToString())
+    self.write('bar.json', b'{ "provideLibs": ["libbar.so"]}')
+    self.command(['proto', '-s', FileArg('bar.json'), '-o', FileArg('out.pb'), '-a'])
+    pb = LinkerConfig()
+    pb.ParseFromString(self.read('out.pb'))
+    self.assertSetEqual(set(pb.provideLibs), set(['libfoo.so', 'libbar.so']))
+
+
+  def test_Proto_with_force(self):
+    self.write('out.pb', LinkerConfig(provideLibs=['libfoo.so']).SerializeToString())
+    self.write('bar.json', b'{ "provideLibs": ["libbar.so"]}')
+    self.command(['proto', '-s', FileArg('bar.json'), '-o', FileArg('out.pb'), '-f'])
+    pb = LinkerConfig()
+    pb.ParseFromString(self.read('out.pb'))
+    self.assertSetEqual(set(pb.provideLibs), set(['libbar.so']))
+
+
+  def command(self, args):
+    parser = conv_linker_config.GetArgParser()
+    parsed_args = parser.parse_args(self.resolve_paths(args))
+    parsed_args.func(parsed_args)
+
+
+if __name__ == '__main__':
+  unittest.main(verbosity=2)
diff --git a/tests/bp2build_bazel_test.sh b/tests/bp2build_bazel_test.sh
index 1ff1b5b..68d7f8d 100755
--- a/tests/bp2build_bazel_test.sh
+++ b/tests/bp2build_bazel_test.sh
@@ -343,4 +343,29 @@
   run_bazel build --config=android --config=api_bp2build //:empty
 }
 
+# Verify that an *_api_contribution target can refer to an api file from
+# another Bazel package.
+function test_api_export_from_another_bazel_package() {
+  setup
+  # Parent dir Android.bp
+  mkdir -p foo
+  cat > foo/Android.bp << 'EOF'
+cc_library {
+  name: "libfoo",
+  stubs: {
+    symbol_file: "api/libfoo.map.txt",
+  },
+}
+EOF
+  # Child dir Android.bp
+  mkdir -p foo/api
+  cat > foo/api/Android.bp << 'EOF'
+package{}
+EOF
+  touch foo/api/libfoo.map.txt
+  # Run test
+  run_soong api_bp2build
+  run_bazel build --config=android --config=api_bp2build //foo:libfoo.contribution
+}
+
 scan_and_run_tests