diff --git a/android/config.go b/android/config.go index 48700fb21ceb3c6e6f35af63b4b52dd7f2b2d116..7e4d22c12e5c1700cf5f3a0b929f46946c62a65f 100644 --- a/android/config.go +++ b/android/config.go @@ -1359,10 +1359,6 @@ func (c *config) ProductPrivateSepolicyDirs() []string { return c.productVariables.ProductPrivateSepolicyDirs } -func (c *config) MissingUsesLibraries() []string { - return c.productVariables.MissingUsesLibraries -} - func (c *deviceConfig) DeviceArch() string { return String(c.config.productVariables.DeviceArch) } diff --git a/android/fixture.go b/android/fixture.go index fd051a7a4e133c5bc1997831dcdb5ca5e3ea3ad1..376a36a149baa465e9d2c02cc11ec9f7f8549a8e 100644 --- a/android/fixture.go +++ b/android/fixture.go @@ -586,6 +586,18 @@ func FixtureExpectsAllErrorsToMatchAPattern(patterns []string) FixtureErrorHandl }) } +// FixtureExpectsOneErrorPattern returns an error handler that will cause the test to fail +// if there is more than one error or the error does not match the pattern. +// +// If the test fails this handler will call `result.FailNow()` which will exit the goroutine within +// which the test is being run which means that the RunTest() method will not return. +func FixtureExpectsOneErrorPattern(pattern string) FixtureErrorHandler { + return FixtureCustomErrorHandler(func(t *testing.T, result *TestResult) { + t.Helper() + CheckErrorsAgainstExpectations(t, result.Errs, []string{pattern}) + }) +} + // FixtureCustomErrorHandler creates a custom error handler func FixtureCustomErrorHandler(function func(t *testing.T, result *TestResult)) FixtureErrorHandler { return simpleErrorHandler{ diff --git a/android/variable.go b/android/variable.go index f84ba82b3184fb492d859e330f436a5928c1c038..19d305a6da951d5ec50c9c7f8133e38521906a79 100644 --- a/android/variable.go +++ b/android/variable.go @@ -374,8 +374,6 @@ type productVariables struct { TargetFSConfigGen []string `json:",omitempty"` - MissingUsesLibraries []string `json:",omitempty"` - EnforceProductPartitionInterface *bool `json:",omitempty"` EnforceInterPartitionJavaSdkLibrary *bool `json:",omitempty"` diff --git a/etc/Android.bp b/etc/Android.bp index cab7389b6e22617d7f116a1a37a8d5707e1baf9a..97788e48879f1ffae3f4a16eba9f2293462be408 100644 --- a/etc/Android.bp +++ b/etc/Android.bp @@ -12,9 +12,11 @@ bootstrap_go_package { ], srcs: [ "prebuilt_etc.go", + "install_symlink.go", ], testSrcs: [ "prebuilt_etc_test.go", + "install_symlink_test.go", ], pluginFor: ["soong_build"], } diff --git a/etc/install_symlink.go b/etc/install_symlink.go new file mode 100644 index 0000000000000000000000000000000000000000..8c1966872978fd8710ef14e18d33efcf5b1be950 --- /dev/null +++ b/etc/install_symlink.go @@ -0,0 +1,92 @@ +// 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 etc + +import ( + "android/soong/android" + "path/filepath" + "strings" +) + +func init() { + RegisterInstallSymlinkBuildComponents(android.InitRegistrationContext) +} + +func RegisterInstallSymlinkBuildComponents(ctx android.RegistrationContext) { + ctx.RegisterModuleType("install_symlink", InstallSymlinkFactory) +} + +// install_symlink can be used to install an symlink with an arbitrary target to an arbitrary path +// on the device. +func InstallSymlinkFactory() android.Module { + module := &InstallSymlink{} + module.AddProperties(&module.properties) + android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon) + return module +} + +type InstallSymlinkProperties struct { + // Where to install this symlink, relative to the partition it's installed on. + // Which partition it's installed on can be controlled by the vendor, system_ext, ramdisk, etc. + // properties. + Installed_location string + // The target of the symlink, aka where the symlink points. + Symlink_target string +} + +type InstallSymlink struct { + android.ModuleBase + properties InstallSymlinkProperties + + output android.Path + installedPath android.InstallPath +} + +func (m *InstallSymlink) GenerateAndroidBuildActions(ctx android.ModuleContext) { + if filepath.Clean(m.properties.Symlink_target) != m.properties.Symlink_target { + ctx.PropertyErrorf("symlink_target", "Should be a clean filepath") + return + } + if filepath.Clean(m.properties.Installed_location) != m.properties.Installed_location { + ctx.PropertyErrorf("installed_location", "Should be a clean filepath") + return + } + if strings.HasPrefix(m.properties.Installed_location, "../") || strings.HasPrefix(m.properties.Installed_location, "/") { + ctx.PropertyErrorf("installed_location", "Should not start with / or ../") + return + } + + out := android.PathForModuleOut(ctx, "out.txt") + android.WriteFileRule(ctx, out, "") + m.output = out + + name := filepath.Base(m.properties.Installed_location) + installDir := android.PathForModuleInstall(ctx, filepath.Dir(m.properties.Installed_location)) + m.installedPath = ctx.InstallAbsoluteSymlink(installDir, name, m.properties.Symlink_target) +} + +func (m *InstallSymlink) AndroidMkEntries() []android.AndroidMkEntries { + return []android.AndroidMkEntries{{ + Class: "FAKE", + // Need at least one output file in order for this to take effect. + OutputFile: android.OptionalPathForPath(m.output), + Include: "$(BUILD_PHONY_PACKAGE)", + ExtraEntries: []android.AndroidMkExtraEntriesFunc{ + func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) { + entries.AddStrings("LOCAL_SOONG_INSTALL_SYMLINKS", m.installedPath.String()) + }, + }, + }} +} diff --git a/etc/install_symlink_test.go b/etc/install_symlink_test.go new file mode 100644 index 0000000000000000000000000000000000000000..d7165e5de0840de6e8da09f75a70f73a9522bc60 --- /dev/null +++ b/etc/install_symlink_test.go @@ -0,0 +1,135 @@ +// 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 etc + +import ( + "android/soong/android" + "strings" + "testing" +) + +var prepareForInstallSymlinkTest = android.GroupFixturePreparers( + android.PrepareForTestWithArchMutator, + android.FixtureRegisterWithContext(RegisterInstallSymlinkBuildComponents), +) + +func TestInstallSymlinkBasic(t *testing.T) { + result := prepareForInstallSymlinkTest.RunTestWithBp(t, ` + install_symlink { + name: "foo", + installed_location: "bin/foo", + symlink_target: "/system/system_ext/bin/foo", + } + `) + + foo_variants := result.ModuleVariantsForTests("foo") + if len(foo_variants) != 1 { + t.Fatalf("expected 1 variant, got %#v", foo_variants) + } + + foo := result.ModuleForTests("foo", "android_common").Module() + androidMkEntries := android.AndroidMkEntriesForTest(t, result.TestContext, foo) + if len(androidMkEntries) != 1 { + t.Fatalf("expected 1 androidmkentry, got %d", len(androidMkEntries)) + } + + symlinks := androidMkEntries[0].EntryMap["LOCAL_SOONG_INSTALL_SYMLINKS"] + if len(symlinks) != 1 { + t.Fatalf("Expected 1 symlink, got %d", len(symlinks)) + } + + if !strings.HasSuffix(symlinks[0], "system/bin/foo") { + t.Fatalf("Expected symlink install path to end in system/bin/foo, got: %s", symlinks[0]) + } +} + +func TestInstallSymlinkToRecovery(t *testing.T) { + result := prepareForInstallSymlinkTest.RunTestWithBp(t, ` + install_symlink { + name: "foo", + installed_location: "bin/foo", + symlink_target: "/system/system_ext/bin/foo", + recovery: true, + } + `) + + foo_variants := result.ModuleVariantsForTests("foo") + if len(foo_variants) != 1 { + t.Fatalf("expected 1 variant, got %#v", foo_variants) + } + + foo := result.ModuleForTests("foo", "android_common").Module() + androidMkEntries := android.AndroidMkEntriesForTest(t, result.TestContext, foo) + if len(androidMkEntries) != 1 { + t.Fatalf("expected 1 androidmkentry, got %d", len(androidMkEntries)) + } + + symlinks := androidMkEntries[0].EntryMap["LOCAL_SOONG_INSTALL_SYMLINKS"] + if len(symlinks) != 1 { + t.Fatalf("Expected 1 symlink, got %d", len(symlinks)) + } + + if !strings.HasSuffix(symlinks[0], "recovery/root/system/bin/foo") { + t.Fatalf("Expected symlink install path to end in recovery/root/system/bin/foo, got: %s", symlinks[0]) + } +} + +func TestErrorOnNonCleanTarget(t *testing.T) { + prepareForInstallSymlinkTest. + ExtendWithErrorHandler(android.FixtureExpectsOneErrorPattern("Should be a clean filepath")). + RunTestWithBp(t, ` + install_symlink { + name: "foo", + installed_location: "bin/foo", + symlink_target: "/system/system_ext/../bin/foo", + } + `) +} + +func TestErrorOnNonCleanInstalledLocation(t *testing.T) { + prepareForInstallSymlinkTest. + ExtendWithErrorHandler(android.FixtureExpectsOneErrorPattern("Should be a clean filepath")). + RunTestWithBp(t, ` + install_symlink { + name: "foo", + installed_location: "bin/../foo", + symlink_target: "/system/system_ext/bin/foo", + } + `) +} + +func TestErrorOnInstalledPathStartingWithDotDot(t *testing.T) { + prepareForInstallSymlinkTest. + ExtendWithErrorHandler(android.FixtureExpectsOneErrorPattern("Should not start with / or \\.\\./")). + RunTestWithBp(t, ` + install_symlink { + name: "foo", + installed_location: "../bin/foo", + symlink_target: "/system/system_ext/bin/foo", + } + `) +} + +func TestErrorOnInstalledPathStartingWithSlash(t *testing.T) { + prepareForInstallSymlinkTest. + ExtendWithErrorHandler(android.FixtureExpectsOneErrorPattern("Should not start with / or \\.\\./")). + RunTestWithBp(t, ` + install_symlink { + name: "foo", + installed_location: "/bin/foo", + symlink_target: "/system/system_ext/bin/foo", + } + `) +} diff --git a/etc/prebuilt_etc_test.go b/etc/prebuilt_etc_test.go index 354f6bb6ea74fa9dc123bc07b0b41a2128f964e9..662184c226719ecf0efa4be20382e5a192275c76 100644 --- a/etc/prebuilt_etc_test.go +++ b/etc/prebuilt_etc_test.go @@ -66,7 +66,7 @@ func TestPrebuiltEtcVariants(t *testing.T) { baz_variants := result.ModuleVariantsForTests("baz.conf") if len(baz_variants) != 1 { - t.Errorf("expected 1, got %#v", bar_variants) + t.Errorf("expected 1, got %#v", baz_variants) } } diff --git a/java/app.go b/java/app.go index 18389d39e3049244d27f456318d9b11bc6ba1831..32be5b518a06fda2c2435eeef406cb801a015eee 100755 --- a/java/app.go +++ b/java/app.go @@ -18,6 +18,7 @@ package java // related module types, including their override variants. import ( + "fmt" "path/filepath" "sort" "strings" @@ -576,9 +577,21 @@ func processMainCert(m android.ModuleBase, certPropValue string, certificates [] var mainCert Certificate if certPropValue != "" { defaultDir := ctx.Config().DefaultAppCertificateDir(ctx) - mainCert = Certificate{ - Pem: defaultDir.Join(ctx, certPropValue+".x509.pem"), - Key: defaultDir.Join(ctx, certPropValue+".pk8"), + + userKeyBasePath := "user-keys/" + certPropValue + userPemPath := android.ExistentPathForSource(ctx, userKeyBasePath+".x509.pem") + userKeyPath := android.ExistentPathForSource(ctx, userKeyBasePath+".pk8") + + if certPropValue == "platform" && userPemPath.Valid() && userKeyPath.Valid() { + mainCert = Certificate{ + Pem: userPemPath.Path(), + Key: userKeyPath.Path(), + } + } else { + mainCert = Certificate{ + Pem: defaultDir.Join(ctx, certPropValue+".x509.pem"), + Key: defaultDir.Join(ctx, certPropValue+".pk8"), + } } } else { pem, key := ctx.Config().DefaultAppCertificate(ctx) @@ -1235,10 +1248,15 @@ func (u *usesLibrary) deps(ctx android.BottomUpMutatorContext, hasFrameworkLibs } } -// presentOptionalUsesLibs returns optional_uses_libs after filtering out MissingUsesLibraries, which don't exist in the -// build. +// presentOptionalUsesLibs returns optional_uses_libs after filtering out libraries that don't exist in the source tree. func (u *usesLibrary) presentOptionalUsesLibs(ctx android.BaseModuleContext) []string { - optionalUsesLibs, _ := android.FilterList(u.usesLibraryProperties.Optional_uses_libs, ctx.Config().MissingUsesLibraries()) + optionalUsesLibs := android.FilterListPred(u.usesLibraryProperties.Optional_uses_libs, func(s string) bool { + exists := ctx.OtherModuleExists(s) + if !exists { + fmt.Printf("Warning: Module '%s' depends on non-existing optional_uses_libs '%s'\n", ctx.ModuleName(), s) + } + return exists + }) return optionalUsesLibs } diff --git a/java/app_import.go b/java/app_import.go index 3371e8e1fe698f818129f301725ca887388298ba..3ff1e4371a871f842ea808808351ea56e1b52517 100644 --- a/java/app_import.go +++ b/java/app_import.go @@ -99,6 +99,9 @@ type AndroidAppImportProperties struct { // If set, create package-export.apk, which other packages can // use to get PRODUCT-agnostic resource data like IDs and type definitions. Export_package_resources *bool + + // Whether the prebuilt apk can be installed without additional processing. Default is false. + Preprocessed *bool } func (a *AndroidAppImport) IsInstallable() bool { diff --git a/java/app_test.go b/java/app_test.go index 372266ef8f3e5d51c542a77a573ee299f97a3907..4d7bd7af07a2a3f78ea8421ad4eb088e2cf3834b 100644 --- a/java/app_test.go +++ b/java/app_test.go @@ -2342,9 +2342,6 @@ func TestUsesLibraries(t *testing.T) { prepareForJavaTest, PrepareForTestWithJavaSdkLibraryFiles, FixtureWithLastReleaseApis("runtime-library", "foo", "quuz", "qux", "bar", "fred"), - android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { - variables.MissingUsesLibraries = []string{"baz"} - }), ).RunTestWithBp(t, bp) app := result.ModuleForTests("app", "android_common")