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

Commit 5c998b9f authored by Jooyung Han's avatar Jooyung Han
Browse files

Add "provide_cpp_shared_libs/uses" props to "apex"

For APEXes to share C++ native libraries, we need a new kind of depedency
between APEXes: "providing" APEXes and "using" APEXes. To reflect this
dependency two new properties are added.

provide_cpp_shared_libs: bool
  this indicates that the current APEX module provides the native C++ shared
  libs to other APEXes.

uses: []string
  this indicates that the current APEX module uses the native C++ shared
  libraries from APEXes listed.

With these two, "using" APEXes can omit shared libraries in its APEX
bundle and use them from the "providing" APEXes.

Note that without corresponding changes in ld.config.txt, this won't
work.(The linker namespaces should be configured so that user APEX can
access provided libs.)

Bug: 136975105
Test: m nothing (this will trigger soong's test)
Change-Id: Iec6f9f67bcbde01145acc383f862ba21c8197536
parent cfec40c4
Loading
Loading
Loading
Loading
+42 −0
Original line number Diff line number Diff line
@@ -111,6 +111,7 @@ var (
	testTag        = dependencyTag{name: "test"}
	keyTag         = dependencyTag{name: "key"}
	certificateTag = dependencyTag{name: "certificate"}
	usesTag        = dependencyTag{name: "uses"}
)

func init() {
@@ -147,6 +148,7 @@ func init() {
	android.PostDepsMutators(func(ctx android.RegisterMutatorsContext) {
		ctx.TopDown("apex_deps", apexDepsMutator)
		ctx.BottomUp("apex", apexMutator).Parallel()
		ctx.BottomUp("apex_uses", apexUsesMutator).Parallel()
	})
}

@@ -187,6 +189,11 @@ func apexMutator(mctx android.BottomUpMutatorContext) {
		mctx.CreateVariations(apexBundleName)
	}
}
func apexUsesMutator(mctx android.BottomUpMutatorContext) {
	if ab, ok := mctx.Module().(*apexBundle); ok {
		mctx.AddFarVariationDependencies(nil, usesTag, ab.properties.Uses...)
	}
}

type apexNativeDependencies struct {
	// List of native libraries
@@ -272,6 +279,12 @@ type apexBundleProperties struct {

	// List of sanitizer names that this APEX is enabled for
	SanitizerNames []string `blueprint:"mutated"`

	// Indicates this APEX provides C++ shared libaries to other APEXes. Default: false.
	Provide_cpp_shared_libs *bool

	// List of providing APEXes' names so that this APEX can depend on provided shared libraries.
	Uses []string
}

type apexTargetBundleProperties struct {
@@ -727,6 +740,30 @@ func (a *apexBundle) GenerateAndroidBuildActions(ctx android.ModuleContext) {

	handleSpecialLibs := !android.Bool(a.properties.Ignore_system_library_special_case)

	// Check if "uses" requirements are met with dependent apexBundles
	var providedNativeSharedLibs []string
	useVendor := proptools.Bool(a.properties.Use_vendor)
	ctx.VisitDirectDepsBlueprint(func(m blueprint.Module) {
		if ctx.OtherModuleDependencyTag(m) != usesTag {
			return
		}
		otherName := ctx.OtherModuleName(m)
		other, ok := m.(*apexBundle)
		if !ok {
			ctx.PropertyErrorf("uses", "%q is not a provider", otherName)
			return
		}
		if proptools.Bool(other.properties.Use_vendor) != useVendor {
			ctx.PropertyErrorf("use_vendor", "%q has different value of use_vendor", otherName)
			return
		}
		if !proptools.Bool(other.properties.Provide_cpp_shared_libs) {
			ctx.PropertyErrorf("uses", "%q does not provide native_shared_libs", otherName)
			return
		}
		providedNativeSharedLibs = append(providedNativeSharedLibs, other.properties.Native_shared_libs...)
	})

	ctx.WalkDepsBlueprint(func(child, parent blueprint.Module) bool {
		if _, ok := parent.(*apexBundle); ok {
			// direct dependencies
@@ -815,6 +852,11 @@ 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 android.InList(cc.Name(), providedNativeSharedLibs) {
						// If we're using a shared library which is provided from other APEX,
						// don't include it in this APEX
						return false
					}
					if !a.Host() && (cc.IsStubs() || cc.HasStubsVariants()) {
						// If the dependency is a stubs lib, don't include it in this APEX,
						// but make sure that the lib is installed on the device.
+173 −5
Original line number Diff line number Diff line
@@ -29,7 +29,32 @@ import (

var buildDir string

func testApexError(t *testing.T, pattern, bp string) {
	ctx, config := testApexContext(t, bp)
	_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
	if len(errs) > 0 {
		android.FailIfNoMatchingErrors(t, pattern, errs)
		return
	}
	_, errs = ctx.PrepareBuildActions(config)
	if len(errs) > 0 {
		android.FailIfNoMatchingErrors(t, pattern, errs)
		return
	}

	t.Fatalf("missing expected error %q (0 errors are returned)", pattern)
}

func testApex(t *testing.T, bp string) *android.TestContext {
	ctx, config := testApexContext(t, bp)
	_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
	android.FailIfErrored(t, errs)
	_, errs = ctx.PrepareBuildActions(config)
	android.FailIfErrored(t, errs)
	return ctx
}

func testApexContext(t *testing.T, bp string) (*android.TestContext, android.Config) {
	config := android.TestArchConfig(buildDir, nil)
	config.TestProductVariables.DeviceVndkVersion = proptools.StringPtr("current")
	config.TestProductVariables.DefaultAppCertificate = proptools.StringPtr("vendor/foo/devkeys/test")
@@ -48,6 +73,7 @@ func testApex(t *testing.T, bp string) *android.TestContext {
	ctx.PostDepsMutators(func(ctx android.RegisterMutatorsContext) {
		ctx.TopDown("apex_deps", apexDepsMutator)
		ctx.BottomUp("apex", apexMutator)
		ctx.BottomUp("apex_uses", apexUsesMutator)
		ctx.TopDown("prebuilt_select", android.PrebuiltSelectModuleMutator).Parallel()
		ctx.BottomUp("prebuilt_postdeps", android.PrebuiltPostDepsMutator).Parallel()
	})
@@ -168,8 +194,10 @@ func testApex(t *testing.T, bp string) *android.TestContext {
		"system/sepolicy/apex/myapex-file_contexts":         nil,
		"system/sepolicy/apex/myapex_keytest-file_contexts": nil,
		"system/sepolicy/apex/otherapex-file_contexts":      nil,
		"system/sepolicy/apex/commonapex-file_contexts":     nil,
		"mylib.cpp":                            nil,
		"mytest.cpp":                           nil,
		"mylib_common.cpp":                     nil,
		"myprebuilt":                           nil,
		"my_include":                           nil,
		"vendor/foo/devkeys/test.x509.pem":     nil,
@@ -188,12 +216,8 @@ func testApex(t *testing.T, bp string) *android.TestContext {
		"myapex-arm.apex":                      nil,
		"frameworks/base/api/current.txt":      nil,
	})
	_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
	android.FailIfErrored(t, errs)
	_, errs = ctx.PrepareBuildActions(config)
	android.FailIfErrored(t, errs)

	return ctx
	return ctx, config
}

func setUp() {
@@ -210,6 +234,7 @@ func tearDown() {

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

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

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

func ensureListNotContains(t *testing.T, result []string, notExpected string) {
	t.Helper()
	if android.InList(notExpected, result) {
		t.Errorf("%q is found in %v", notExpected, result)
	}
@@ -789,6 +817,30 @@ func TestUseVendor(t *testing.T) {
	ensureNotContains(t, inputsString, "android_arm64_armv8-a_core_shared_myapex/mylib2.so")
}

func TestUseVendorFailsIfNotVendorAvailable(t *testing.T) {
	testApexError(t, `dependency "mylib" of "myapex" missing variant:\n.*image:vendor`, `
		apex {
			name: "myapex",
			key: "myapex.key",
			native_shared_libs: ["mylib"],
			use_vendor: true,
		}

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

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

func TestStaticLinking(t *testing.T) {
	ctx := testApex(t, `
		apex {
@@ -1321,6 +1373,122 @@ func TestApexWithTests(t *testing.T) {
	ensureContains(t, copyCmds, "image.apex/bin/test/mytest")
}

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

		apex {
			name: "commonapex",
			key: "myapex.key",
			native_shared_libs: ["libcommon"],
			provide_cpp_shared_libs: true,
		}

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

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

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

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

	module2 := ctx.ModuleForTests("commonapex", "android_common_commonapex")
	apexRule2 := module2.Rule("apexRule")
	copyCmds2 := apexRule2.Args["copy_commands"]

	ensureListContains(t, ctx.ModuleVariantsForTests("mylib"), "android_arm64_armv8-a_core_shared_myapex")
	ensureListContains(t, ctx.ModuleVariantsForTests("libcommon"), "android_arm64_armv8-a_core_shared_commonapex")
	ensureContains(t, copyCmds1, "image.apex/lib64/mylib.so")
	ensureContains(t, copyCmds2, "image.apex/lib64/libcommon.so")
	ensureNotContains(t, copyCmds1, "image.apex/lib64/libcommon.so")
}

func TestApexUsesFailsIfNotProvided(t *testing.T) {
	testApexError(t, `uses: "commonapex" does not provide native_shared_libs`, `
		apex {
			name: "myapex",
			key: "myapex.key",
			uses: ["commonapex"],
		}

		apex {
			name: "commonapex",
			key: "myapex.key",
		}

		apex_key {
			name: "myapex.key",
			public_key: "testkey.avbpubkey",
			private_key: "testkey.pem",
		}
	`)
	testApexError(t, `uses: "commonapex" is not a provider`, `
		apex {
			name: "myapex",
			key: "myapex.key",
			uses: ["commonapex"],
		}

		cc_library {
			name: "commonapex",
			system_shared_libs: [],
			stl: "none",
		}

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

func TestApexUsesFailsIfUseVenderMismatch(t *testing.T) {
	testApexError(t, `use_vendor: "commonapex" has different value of use_vendor`, `
		apex {
			name: "myapex",
			key: "myapex.key",
			use_vendor: true,
			uses: ["commonapex"],
		}

		apex {
			name: "commonapex",
			key: "myapex.key",
			provide_cpp_shared_libs: true,
		}

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

func TestMain(m *testing.M) {
	run := func() int {
		setUp()