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

Commit 25fc6a9c authored by Jiyong Park's avatar Jiyong Park
Browse files

Stubs variant is used when building for APEX

When a native module is built for an APEX and is depending on a native
library having stubs (i.e. stubs.versions property is set), the stubs
variant is used unless the dependent lib is directly included in the
same APEX with the depending module.

Example:

apex {
    name: "myapex",
    native_shared_libs: ["libX", "libY"],
}

cc_library {
    name: "libX",
    shared_libs: ["libY", "libZ"],
}

cc_library {
    name: "libY",
    stubs: { versions: ["1", "2"], },
}

cc_library {
    name: "libZ",
    stubs: { versions: ["1", "2"], },
}

In this case, libX is linking to the impl variant of libY (that provides
private APIs) while libY is linking to the version 2 stubs of libZ. This is
because libY is directly included in the same apex via
native_shared_libs property, but libZ isn't.

Bug: 112672359
Test: apex_test added
Change-Id: If9871b70dc74a06bd828dd4cd1aeebd2e68b837c
parent 2098eb8c
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -353,6 +353,9 @@ bootstrap_go_package {
        "apex/apex.go",
        "apex/key.go",
    ],
    testSrcs: [
        "apex/apex_test.go",
    ],
    pluginFor: ["soong_build"],
}

+64 −0
Original line number Diff line number Diff line
@@ -14,6 +14,8 @@

package android

import "sync"

// ApexModule is the interface that a module type is expected to implement if
// the module has to be built differently depending on whether the module
// is destined for an apex or not (installed to one of the regular partitions).
@@ -94,6 +96,68 @@ func (m *ApexModuleBase) IsInstallableToApex() bool {
	return false
}

// This structure maps a module name to the set of APEX bundle names that the module
// should be built for. Examples:
//
// ...["foo"]["bar"] == true: module foo is directly depended on by APEX bar
// ...["foo"]["bar"] == false: module foo is indirectly depended on by APEX bar
// ...["foo"]["bar"] doesn't exist: foo is not built for APEX bar
// ...["foo"] doesn't exist: foo is not built for any APEX
func apexBundleNamesMap(config Config) map[string]map[string]bool {
	return config.Once("apexBundleNames", func() interface{} {
		return make(map[string]map[string]bool)
	}).(map[string]map[string]bool)
}

var bundleNamesMapMutex sync.Mutex

// Mark that a module named moduleName should be built for an apex named bundleName
// directDep should be set to true if the module is a direct dependency of the apex.
func BuildModuleForApexBundle(ctx BaseModuleContext, moduleName string, bundleName string, directDep bool) {
	bundleNamesMapMutex.Lock()
	defer bundleNamesMapMutex.Unlock()
	bundleNames, ok := apexBundleNamesMap(ctx.Config())[moduleName]
	if !ok {
		bundleNames = make(map[string]bool)
		apexBundleNamesMap(ctx.Config())[moduleName] = bundleNames
	}
	bundleNames[bundleName] = bundleNames[bundleName] || directDep
}

// Returns the list of apex bundle names that the module named moduleName
// should be built for.
func GetApexBundlesForModule(ctx BaseModuleContext, moduleName string) map[string]bool {
	bundleNamesMapMutex.Lock()
	defer bundleNamesMapMutex.Unlock()
	return apexBundleNamesMap(ctx.Config())[moduleName]
}

// Tests if moduleName is directly depended on by bundleName (i.e. referenced in
// native_shared_libs, etc.)
func DirectlyInApex(config Config, bundleName string, moduleName string) bool {
	bundleNamesMapMutex.Lock()
	defer bundleNamesMapMutex.Unlock()
	if bundleNames, ok := apexBundleNamesMap(config)[moduleName]; ok {
		return bundleNames[bundleName]
	}
	return false
}

// Tests if moduleName is directly depended on by any APEX. If this returns true,
// that means the module is part of the platform.
func DirectlyInAnyApex(config Config, moduleName string) bool {
	bundleNamesMapMutex.Lock()
	defer bundleNamesMapMutex.Unlock()
	if bundleNames, ok := apexBundleNamesMap(config)[moduleName]; ok {
		for bn := range bundleNames {
			if bundleNames[bn] {
				return true
			}
		}
	}
	return false
}

func InitApexModule(m ApexModule) {
	base := m.apexModuleBase()
	base.canHaveApexVariants = true
+8 −14
Original line number Diff line number Diff line
@@ -128,13 +128,6 @@ func init() {
	})
}

// maps a module name to set of apex bundle names that the module should be built for
func apexBundleNamesFor(config android.Config) map[string]map[string]bool {
	return config.Once("apexBundleNames", func() interface{} {
		return make(map[string]map[string]bool)
	}).(map[string]map[string]bool)
}

// Mark the direct and transitive dependencies of apex bundles so that they
// can be built for the apex bundles.
func apexDepsMutator(mctx android.TopDownMutatorContext) {
@@ -143,12 +136,9 @@ func apexDepsMutator(mctx android.TopDownMutatorContext) {
		mctx.WalkDeps(func(child, parent android.Module) bool {
			if am, ok := child.(android.ApexModule); ok && am.CanHaveApexVariants() {
				moduleName := mctx.OtherModuleName(am) + "-" + am.Target().String()
				bundleNames, ok := apexBundleNamesFor(mctx.Config())[moduleName]
				if !ok {
					bundleNames = make(map[string]bool)
					apexBundleNamesFor(mctx.Config())[moduleName] = bundleNames
				}
				bundleNames[apexBundleName] = true
				// If the parent is apexBundle, this child is directly depended.
				_, directDep := parent.(*apexBundle)
				android.BuildModuleForApexBundle(mctx, moduleName, apexBundleName, directDep)
				return true
			} else {
				return false
@@ -161,7 +151,8 @@ func apexDepsMutator(mctx android.TopDownMutatorContext) {
func apexMutator(mctx android.BottomUpMutatorContext) {
	if am, ok := mctx.Module().(android.ApexModule); ok && am.CanHaveApexVariants() {
		moduleName := mctx.ModuleName() + "-" + am.Target().String()
		if bundleNames, ok := apexBundleNamesFor(mctx.Config())[moduleName]; ok {
		bundleNames := android.GetApexBundlesForModule(mctx, moduleName)
		if len(bundleNames) > 0 {
			variations := []string{"platform"}
			for bn := range bundleNames {
				variations = append(variations, bn)
@@ -495,6 +486,9 @@ func (a *apexBundle) GenerateAndroidBuildActions(ctx android.ModuleContext) {
			// indirect dependencies
			if am, ok := child.(android.ApexModule); ok && am.CanHaveApexVariants() && am.IsInstallableToApex() {
				if cc, ok := child.(*cc.Module); ok {
					if cc.IsStubs() || cc.HasStubsVariants() {
						return false
					}
					depName := ctx.OtherModuleName(child)
					fileToCopy, dirInApex := getCopyManifestForNativeLibrary(cc)
					filesInfo = append(filesInfo, apexFile{fileToCopy, depName, cc.Arch().ArchType, dirInApex, nativeSharedLib})

apex/apex_test.go

0 → 100644
+369 −0
Original line number Diff line number Diff line
// Copyright 2018 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 apex

import (
	"android/soong/android"
	"android/soong/cc"
	"io/ioutil"
	"os"
	"strings"
	"testing"
)

func testApex(t *testing.T, bp string) *android.TestContext {
	config, buildDir := setup(t)
	defer teardown(buildDir)

	ctx := android.NewTestArchContext()
	ctx.RegisterModuleType("apex", android.ModuleFactoryAdaptor(apexBundleFactory))
	ctx.RegisterModuleType("apex_key", android.ModuleFactoryAdaptor(apexKeyFactory))

	ctx.PostDepsMutators(func(ctx android.RegisterMutatorsContext) {
		ctx.TopDown("apex_deps", apexDepsMutator)
		ctx.BottomUp("apex", apexMutator)
	})

	ctx.RegisterModuleType("cc_library", android.ModuleFactoryAdaptor(cc.LibraryFactory))
	ctx.RegisterModuleType("cc_library_shared", android.ModuleFactoryAdaptor(cc.LibrarySharedFactory))
	ctx.RegisterModuleType("cc_object", android.ModuleFactoryAdaptor(cc.ObjectFactory))
	ctx.RegisterModuleType("toolchain_library", android.ModuleFactoryAdaptor(cc.ToolchainLibraryFactory))
	ctx.PreDepsMutators(func(ctx android.RegisterMutatorsContext) {
		ctx.BottomUp("link", cc.LinkageMutator).Parallel()
		ctx.BottomUp("version", cc.VersionMutator).Parallel()
		ctx.BottomUp("begin", cc.BeginMutator).Parallel()
	})

	ctx.Register()

	bp = bp + `
		toolchain_library {
			name: "libcompiler_rt-extras",
			src: "",
		}

		toolchain_library {
			name: "libatomic",
			src: "",
		}

		toolchain_library {
			name: "libgcc",
			src: "",
		}

		toolchain_library {
			name: "libclang_rt.builtins-aarch64-android",
			src: "",
		}

		toolchain_library {
			name: "libclang_rt.builtins-arm-android",
			src: "",
		}

		cc_object {
			name: "crtbegin_so",
			stl: "none",
		}

		cc_object {
			name: "crtend_so",
			stl: "none",
		}

	`

	ctx.MockFileSystem(map[string][]byte{
		"Android.bp":                                []byte(bp),
		"testkey.avbpubkey":                         nil,
		"testkey.pem":                               nil,
		"build/target/product/security":             nil,
		"apex_manifest.json":                        nil,
		"system/sepolicy/apex/myapex-file_contexts": nil,
		"mylib.cpp":                                 nil,
	})
	_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
	android.FailIfErrored(t, errs)
	_, errs = ctx.PrepareBuildActions(config)
	android.FailIfErrored(t, errs)

	return ctx
}

func setup(t *testing.T) (config android.Config, buildDir string) {
	buildDir, err := ioutil.TempDir("", "soong_apex_test")
	if err != nil {
		t.Fatal(err)
	}

	config = android.TestArchConfig(buildDir, nil)

	return
}

func teardown(buildDir string) {
	os.RemoveAll(buildDir)
}

// ensure that 'result' contains 'expected'
func ensureContains(t *testing.T, result string, expected string) {
	if !strings.Contains(result, expected) {
		t.Errorf("%q is not found in %q", expected, result)
	}
}

// ensures that 'result' does not contain 'notExpected'
func ensureNotContains(t *testing.T, result string, notExpected string) {
	if strings.Contains(result, notExpected) {
		t.Errorf("%q is found in %q", notExpected, result)
	}
}

func ensureListContains(t *testing.T, result []string, expected string) {
	if !android.InList(expected, result) {
		t.Errorf("%q is not found in %v", expected, result)
	}
}

func ensureListNotContains(t *testing.T, result []string, notExpected string) {
	if android.InList(notExpected, result) {
		t.Errorf("%q is found in %v", notExpected, result)
	}
}

// Minimal test
func TestBasicApex(t *testing.T) {
	ctx := testApex(t, `
		apex {
			name: "myapex",
			key: "myapex.key",
			native_shared_libs: ["mylib"],
		}

		apex_key {
			name: "myapex.key",
			public_key: "testkey.avbpubkey",
			private_key: "testkey.pem",
		}

		cc_library {
			name: "mylib",
			srcs: ["mylib.cpp"],
			shared_libs: ["mylib2"],
			system_shared_libs: [],
			stl: "none",
		}

		cc_library {
			name: "mylib2",
			srcs: ["mylib.cpp"],
			system_shared_libs: [],
			stl: "none",
		}
	`)

	apexRule := ctx.ModuleForTests("myapex", "android_common_myapex").Rule("apexRule")
	copyCmds := apexRule.Args["copy_commands"]

	// Ensure that main rule creates an output
	ensureContains(t, apexRule.Output.String(), "myapex.apex.unsigned")

	// Ensure that apex variant is created for the direct dep
	ensureListContains(t, ctx.ModuleVariantsForTests("mylib"), "android_arm64_armv8-a_shared_myapex")

	// Ensure that apex variant is created for the indirect dep
	ensureListContains(t, ctx.ModuleVariantsForTests("mylib2"), "android_arm64_armv8-a_shared_myapex")

	// Ensure that both direct and indirect deps are copied into apex
	ensureContains(t, copyCmds, "image/lib64/mylib.so")
	ensureContains(t, copyCmds, "image/lib64/mylib2.so")
}

func TestApexWithStubs(t *testing.T) {
	ctx := testApex(t, `
		apex {
			name: "myapex",
			key: "myapex.key",
			native_shared_libs: ["mylib", "mylib3"],
		}

		apex_key {
			name: "myapex.key",
			public_key: "testkey.avbpubkey",
			private_key: "testkey.pem",
		}

		cc_library {
			name: "mylib",
			srcs: ["mylib.cpp"],
			shared_libs: ["mylib2", "mylib3"],
			system_shared_libs: [],
			stl: "none",
		}

		cc_library {
			name: "mylib2",
			srcs: ["mylib.cpp"],
			system_shared_libs: [],
			stl: "none",
			stubs: {
				versions: ["1", "2", "3"],
			},
		}

		cc_library {
			name: "mylib3",
				srcs: ["mylib.cpp"],
				system_shared_libs: [],
			stl: "none",
			stubs: {
				versions: ["10", "11", "12"],
			},
		}
	`)

	apexRule := ctx.ModuleForTests("myapex", "android_common_myapex").Rule("apexRule")
	copyCmds := apexRule.Args["copy_commands"]

	// Ensure that direct non-stubs dep is always included
	ensureContains(t, copyCmds, "image/lib64/mylib.so")

	// Ensure that indirect stubs dep is not included
	ensureNotContains(t, copyCmds, "image/lib64/mylib2.so")

	// Ensure that direct stubs dep is included
	ensureContains(t, copyCmds, "image/lib64/mylib3.so")

	mylibLdFlags := ctx.ModuleForTests("mylib", "android_arm64_armv8-a_shared_myapex").Rule("ld").Args["libFlags"]

	// Ensure that mylib is linking with the latest version of stubs for mylib2
	ensureContains(t, mylibLdFlags, "mylib2/android_arm64_armv8-a_shared_3_myapex/mylib2.so")
	// ... and not linking to the non-stub (impl) variant of mylib2
	ensureNotContains(t, mylibLdFlags, "mylib2/android_arm64_armv8-a_shared_myapex/mylib2.so")

	// Ensure that mylib is linking with the non-stub (impl) of mylib3 (because mylib3 is in the same apex)
	ensureContains(t, mylibLdFlags, "mylib3/android_arm64_armv8-a_shared_myapex/mylib3.so")
	// .. and not linking to the stubs variant of mylib3
	ensureNotContains(t, mylibLdFlags, "mylib3/android_arm64_armv8-a_shared_12_myapex/mylib3.so")
}

func TestApexWithSystemLibsStubs(t *testing.T) {
	ctx := testApex(t, `
		apex {
			name: "myapex",
			key: "myapex.key",
			native_shared_libs: ["mylib", "mylib_shared", "libdl", "libm"],
		}

		apex_key {
			name: "myapex.key",
			public_key: "testkey.avbpubkey",
			private_key: "testkey.pem",
		}

		cc_library {
			name: "mylib",
			srcs: ["mylib.cpp"],
			shared_libs: ["libdl#27"],
			stl: "none",
		}

		cc_library_shared {
			name: "mylib_shared",
			srcs: ["mylib.cpp"],
			shared_libs: ["libdl#27"],
			stl: "none",
		}

		cc_library {
			name: "libc",
			no_libgcc: true,
			nocrt: true,
			system_shared_libs: [],
			stl: "none",
			stubs: {
				versions: ["27", "28", "29"],
			},
		}

		cc_library {
			name: "libm",
			no_libgcc: true,
			nocrt: true,
			system_shared_libs: [],
			stl: "none",
			stubs: {
				versions: ["27", "28", "29"],
			},
		}

		cc_library {
			name: "libdl",
			no_libgcc: true,
			nocrt: true,
			system_shared_libs: [],
			stl: "none",
			stubs: {
				versions: ["27", "28", "29"],
			},
		}
	`)

	apexRule := ctx.ModuleForTests("myapex", "android_common_myapex").Rule("apexRule")
	copyCmds := apexRule.Args["copy_commands"]

	// Ensure that mylib, libm, libdl are included.
	ensureContains(t, copyCmds, "image/lib64/mylib.so")
	ensureContains(t, copyCmds, "image/lib64/libm.so")
	ensureContains(t, copyCmds, "image/lib64/libdl.so")

	// Ensure that libc is not included (since it has stubs and not listed in native_shared_libs)
	ensureNotContains(t, copyCmds, "image/lib64/libc.so")

	mylibLdFlags := ctx.ModuleForTests("mylib", "android_arm64_armv8-a_shared_myapex").Rule("ld").Args["libFlags"]
	mylibCFlags := ctx.ModuleForTests("mylib", "android_arm64_armv8-a_static_myapex").Rule("cc").Args["cFlags"]
	mylibSharedCFlags := ctx.ModuleForTests("mylib_shared", "android_arm64_armv8-a_shared_myapex").Rule("cc").Args["cFlags"]

	// For dependency to libc
	// Ensure that mylib is linking with the latest version of stubs
	ensureContains(t, mylibLdFlags, "libc/android_arm64_armv8-a_shared_29_myapex/libc.so")
	// ... and not linking to the non-stub (impl) variant
	ensureNotContains(t, mylibLdFlags, "libc/android_arm64_armv8-a_shared_myapex/libc.so")
	// ... Cflags from stub is correctly exported to mylib
	ensureContains(t, mylibCFlags, "__LIBC_API__=29")
	ensureContains(t, mylibSharedCFlags, "__LIBC_API__=29")

	// For dependency to libm
	// Ensure that mylib is linking with the non-stub (impl) variant
	ensureContains(t, mylibLdFlags, "libm/android_arm64_armv8-a_shared_myapex/libm.so")
	// ... and not linking to the stub variant
	ensureNotContains(t, mylibLdFlags, "libm/android_arm64_armv8-a_shared_29_myapex/libm.so")
	// ... and is not compiling with the stub
	ensureNotContains(t, mylibCFlags, "__LIBM_API__=29")
	ensureNotContains(t, mylibSharedCFlags, "__LIBM_API__=29")

	// For dependency to libdl
	// Ensure that mylib is linking with the specified version of stubs
	ensureContains(t, mylibLdFlags, "libdl/android_arm64_armv8-a_shared_27_myapex/libdl.so")
	// ... and not linking to the other versions of stubs
	ensureNotContains(t, mylibLdFlags, "libdl/android_arm64_armv8-a_shared_28_myapex/libdl.so")
	ensureNotContains(t, mylibLdFlags, "libdl/android_arm64_armv8-a_shared_29_myapex/libdl.so")
	// ... and not linking to the non-stub (impl) variant
	ensureNotContains(t, mylibLdFlags, "libdl/android_arm64_armv8-a_shared_myapex/libdl.so")
	// ... Cflags from stub is correctly exported to mylib
	ensureContains(t, mylibCFlags, "__LIBDL_API__=27")
	ensureContains(t, mylibSharedCFlags, "__LIBDL_API__=27")
}
+96 −16
Original line number Diff line number Diff line
@@ -39,7 +39,7 @@ func init() {
		ctx.BottomUp("vndk", vndkMutator).Parallel()
		ctx.BottomUp("ndk_api", ndkApiMutator).Parallel()
		ctx.BottomUp("test_per_src", testPerSrcMutator).Parallel()
		ctx.BottomUp("version", versionMutator).Parallel()
		ctx.BottomUp("version", VersionMutator).Parallel()
		ctx.BottomUp("begin", BeginMutator).Parallel()
	})

@@ -248,6 +248,7 @@ type ModuleContextIntf interface {
	getVndkExtendsModuleName() string
	isPgoCompile() bool
	useClangLld(actx ModuleContext) bool
	isApex() bool
}

type ModuleContext interface {
@@ -308,6 +309,8 @@ type dependencyTag struct {
	library bool

	reexportFlags bool

	explicitlyVersioned bool
}

var (
@@ -511,6 +514,20 @@ func (c *Module) onlyInRecovery() bool {
	return c.ModuleBase.InstallInRecovery()
}

func (c *Module) IsStubs() bool {
	if library, ok := c.linker.(*libraryDecorator); ok {
		return library.buildStubs()
	}
	return false
}

func (c *Module) HasStubsVariants() bool {
	if library, ok := c.linker.(*libraryDecorator); ok {
		return len(library.Properties.Stubs.Versions) > 0
	}
	return false
}

type baseModuleContext struct {
	android.BaseContext
	moduleContextImpl
@@ -649,6 +666,11 @@ func (ctx *moduleContextImpl) getVndkExtendsModuleName() string {
	return ctx.mod.getVndkExtendsModuleName()
}

// Tests if this module is built for APEX
func (ctx *moduleContextImpl) isApex() bool {
	return ctx.mod.ApexName() != ""
}

func newBaseModule(hod android.HostOrDeviceSupported, multilib android.Multilib) *Module {
	return &Module{
		hod:      hod,
@@ -1081,6 +1103,30 @@ func (c *Module) DepsMutator(actx android.BottomUpMutatorContext) {
		{Mutator: "link", Variation: "static"},
	}, lateStaticDepTag, deps.LateStaticLibs...)

	addSharedLibDependencies := func(depTag dependencyTag, name string, version string) {
		var variations []blueprint.Variation
		variations = append(variations, blueprint.Variation{Mutator: "link", Variation: "shared"})
		versionVariantAvail := ctx.Os() == android.Android && !ctx.useVndk() && !c.inRecovery()
		if version != "" && versionVariantAvail {
			// Version is explicitly specified. i.e. libFoo#30
			variations = append(variations, blueprint.Variation{Mutator: "version", Variation: version})
			depTag.explicitlyVersioned = true
		}
		actx.AddVariationDependencies(variations, depTag, name)

		// If the version is not specified, add dependency to the latest stubs library.
		// The stubs library will be used when the depending module is built for APEX and
		// the dependent module is not in the same APEX.
		latestVersion := latestStubsVersionFor(actx.Config(), name)
		if version == "" && latestVersion != "" && versionVariantAvail {
			actx.AddVariationDependencies([]blueprint.Variation{
				{Mutator: "link", Variation: "shared"},
				{Mutator: "version", Variation: latestVersion},
			}, depTag, name)
			// Note that depTag.explicitlyVersioned is false in this case.
		}
	}

	// shared lib names without the #version suffix
	var sharedLibNames []string

@@ -1091,29 +1137,17 @@ func (c *Module) DepsMutator(actx android.BottomUpMutatorContext) {
		if inList(lib, deps.ReexportSharedLibHeaders) {
			depTag = sharedExportDepTag
		}
		var variations []blueprint.Variation
		variations = append(variations, blueprint.Variation{Mutator: "link", Variation: "shared"})
		if version != "" && ctx.Os() == android.Android && !ctx.useVndk() && !c.inRecovery() {
			variations = append(variations, blueprint.Variation{Mutator: "version", Variation: version})
		}
		actx.AddVariationDependencies(variations, depTag, name)
		addSharedLibDependencies(depTag, name, version)
	}

	for _, lib := range deps.LateSharedLibs {
		name, version := stubsLibNameAndVersion(lib)
		if inList(name, sharedLibNames) {
		if inList(lib, sharedLibNames) {
			// This is to handle the case that some of the late shared libs (libc, libdl, libm, ...)
			// are added also to SharedLibs with version (e.g., libc#10). If not skipped, we will be
			// linking against both the stubs lib and the non-stubs lib at the same time.
			continue
		}
		depTag := lateSharedDepTag
		var variations []blueprint.Variation
		variations = append(variations, blueprint.Variation{Mutator: "link", Variation: "shared"})
		if version != "" && ctx.Os() == android.Android && !ctx.useVndk() && !c.inRecovery() {
			variations = append(variations, blueprint.Variation{Mutator: "version", Variation: version})
		}
		actx.AddVariationDependencies(variations, depTag, name)
		addSharedLibDependencies(lateSharedDepTag, lib, "")
	}

	actx.AddVariationDependencies([]blueprint.Variation{
@@ -1372,7 +1406,53 @@ func (c *Module) depsToPaths(ctx android.ModuleContext) PathDeps {
				return
			}
		}

		// Extract explicitlyVersioned field from the depTag and reset it inside the struct.
		// Otherwise, sharedDepTag and lateSharedDepTag with explicitlyVersioned set to true
		// won't be matched to sharedDepTag and lateSharedDepTag.
		explicitlyVersioned := false
		if t, ok := depTag.(dependencyTag); ok {
			explicitlyVersioned = t.explicitlyVersioned
			t.explicitlyVersioned = false
			depTag = t
		}

		if t, ok := depTag.(dependencyTag); ok && t.library {
			if dependentLibrary, ok := ccDep.linker.(*libraryDecorator); ok {
				depIsStubs := dependentLibrary.buildStubs()
				depHasStubs := ccDep.HasStubsVariants()
				depNameWithTarget := depName + "-" + ccDep.Target().String()
				depInSameApex := android.DirectlyInApex(ctx.Config(), c.ApexName(), depNameWithTarget)
				depInPlatform := !android.DirectlyInAnyApex(ctx.Config(), depNameWithTarget)

				var useThisDep bool
				if depIsStubs && explicitlyVersioned {
					// Always respect dependency to the versioned stubs (i.e. libX#10)
					useThisDep = true
				} else if !depHasStubs {
					// Use non-stub variant if that is the only choice
					// (i.e. depending on a lib without stubs.version property)
					useThisDep = true
				} else if c.IsForPlatform() {
					// If not building for APEX, use stubs only when it is from
					// an APEX (and not from platform)
					useThisDep = (depInPlatform != depIsStubs)
					if c.inRecovery() {
						// However, for recovery modules, since there is no APEX there,
						// always link to non-stub variant
						useThisDep = !depIsStubs
					}
				} else {
					// If building for APEX, use stubs only when it is not from
					// the same APEX
					useThisDep = (depInSameApex != depIsStubs)
				}

				if !useThisDep {
					return // stop processing this dep
				}
			}

			if i, ok := ccDep.linker.(exportedFlagsProducer); ok {
				flags := i.exportedFlags()
				deps := i.exportedFlagsDeps()
Loading