diff options
56 files changed, 2062 insertions, 590 deletions
diff --git a/Android.bp b/Android.bp index cb2f773a5..2a4653a13 100644 --- a/Android.bp +++ b/Android.bp @@ -195,7 +195,6 @@ bootstrap_go_package { "cc/vendor_snapshot.go", "cc/vndk.go", "cc/vndk_prebuilt.go", - "cc/xom.go", "cc/cflag_artifacts.go", "cc/cmakelists.go", @@ -205,6 +204,7 @@ bootstrap_go_package { "cc/linker.go", "cc/binary.go", + "cc/binary_sdk_member.go", "cc/fuzz.go", "cc/library.go", "cc/library_sdk_member.go", diff --git a/android/apex.go b/android/apex.go index 9195388bf..43a42df9b 100644 --- a/android/apex.go +++ b/android/apex.go @@ -19,6 +19,14 @@ import ( "sync" ) +type ApexInfo struct { + // Name of the apex variant that this module is mutated into + ApexName string + + // Whether this apex variant needs to target Android 10 + LegacyAndroid10Support bool +} + // 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). @@ -38,9 +46,12 @@ type ApexModule interface { Module apexModuleBase() *ApexModuleBase - // Marks that this module should be built for the APEX of the specified name. + // Marks that this module should be built for the specified APEXes. // Call this before apex.apexMutator is run. - BuildForApex(apexName string) + BuildForApexes(apexes []ApexInfo) + + // Returns the APEXes that this module will be built for + ApexVariations() []ApexInfo // Returns the name of APEX that this module will be built for. Empty string // is returned when 'IsForPlatform() == true'. Note that a module can be @@ -66,13 +77,9 @@ type ApexModule interface { IsInstallableToApex() bool // Mutate this module into one or more variants each of which is built - // for an APEX marked via BuildForApex(). + // for an APEX marked via BuildForApexes(). CreateApexVariations(mctx BottomUpMutatorContext) []Module - // Sets the name of the apex variant of this module. Called inside - // CreateApexVariations. - setApexName(apexName string) - // Tests if this module is available for the specified APEX or ":platform" AvailableFor(what string) bool @@ -88,12 +95,10 @@ type ApexProperties struct { // // "//apex_available:anyapex" is a pseudo APEX name that matches to any APEX. // "//apex_available:platform" refers to non-APEX partitions like "system.img". - // Default is ["//apex_available:platform", "//apex_available:anyapex"]. - // TODO(b/128708192) change the default to ["//apex_available:platform"] + // Default is ["//apex_available:platform"]. Apex_available []string - // Name of the apex variant that this module is mutated into - ApexName string `blueprint:"mutated"` + Info ApexInfo `blueprint:"mutated"` } // Provides default implementation for the ApexModule interface. APEX-aware @@ -104,31 +109,37 @@ type ApexModuleBase struct { canHaveApexVariants bool apexVariationsLock sync.Mutex // protects apexVariations during parallel apexDepsMutator - apexVariations []string + apexVariations []ApexInfo } func (m *ApexModuleBase) apexModuleBase() *ApexModuleBase { return m } -func (m *ApexModuleBase) BuildForApex(apexName string) { +func (m *ApexModuleBase) BuildForApexes(apexes []ApexInfo) { m.apexVariationsLock.Lock() defer m.apexVariationsLock.Unlock() - if !InList(apexName, m.apexVariations) { - m.apexVariations = append(m.apexVariations, apexName) +nextApex: + for _, apex := range apexes { + for _, v := range m.apexVariations { + if v.ApexName == apex.ApexName { + continue nextApex + } + } + m.apexVariations = append(m.apexVariations, apex) } } -func (m *ApexModuleBase) ApexName() string { - return m.ApexProperties.ApexName +func (m *ApexModuleBase) ApexVariations() []ApexInfo { + return m.apexVariations } -func (m *ApexModuleBase) IsForPlatform() bool { - return m.ApexProperties.ApexName == "" +func (m *ApexModuleBase) ApexName() string { + return m.ApexProperties.Info.ApexName } -func (m *ApexModuleBase) setApexName(apexName string) { - m.ApexProperties.ApexName = apexName +func (m *ApexModuleBase) IsForPlatform() bool { + return m.ApexProperties.Info.ApexName == "" } func (m *ApexModuleBase) CanHaveApexVariants() bool { @@ -177,25 +188,35 @@ func (m *ApexModuleBase) checkApexAvailableProperty(mctx BaseModuleContext) { } } +type byApexName []ApexInfo + +func (a byApexName) Len() int { return len(a) } +func (a byApexName) Swap(i, j int) { a[i], a[j] = a[j], a[i] } +func (a byApexName) Less(i, j int) bool { return a[i].ApexName < a[j].ApexName } + func (m *ApexModuleBase) CreateApexVariations(mctx BottomUpMutatorContext) []Module { if len(m.apexVariations) > 0 { m.checkApexAvailableProperty(mctx) - sort.Strings(m.apexVariations) + sort.Sort(byApexName(m.apexVariations)) variations := []string{} variations = append(variations, "") // Original variation for platform - variations = append(variations, m.apexVariations...) + for _, apex := range m.apexVariations { + variations = append(variations, apex.ApexName) + } defaultVariation := "" mctx.SetDefaultDependencyVariation(&defaultVariation) modules := mctx.CreateVariations(variations...) - for i, m := range modules { + for i, mod := range modules { platformVariation := i == 0 - if platformVariation && !mctx.Host() && !m.(ApexModule).AvailableFor(AvailableToPlatform) { - m.SkipInstall() + if platformVariation && !mctx.Host() && !mod.(ApexModule).AvailableFor(AvailableToPlatform) { + mod.SkipInstall() + } + if !platformVariation { + mod.(ApexModule).apexModuleBase().ApexProperties.Info = m.apexVariations[i-1] } - m.(ApexModule).setApexName(variations[i]) } return modules } @@ -219,18 +240,20 @@ func apexNamesMap() map[string]map[string]bool { } // Update the map to mark that a module named moduleName is directly or indirectly -// depended on by an APEX named apexName. Directly depending means that a module +// depended on by the specified APEXes. Directly depending means that a module // is explicitly listed in the build definition of the APEX via properties like // native_shared_libs, java_libs, etc. -func UpdateApexDependency(apexName string, moduleName string, directDep bool) { +func UpdateApexDependency(apexes []ApexInfo, moduleName string, directDep bool) { apexNamesMapMutex.Lock() defer apexNamesMapMutex.Unlock() - apexNames, ok := apexNamesMap()[moduleName] - if !ok { - apexNames = make(map[string]bool) - apexNamesMap()[moduleName] = apexNames + for _, apex := range apexes { + apexesForModule, ok := apexNamesMap()[moduleName] + if !ok { + apexesForModule = make(map[string]bool) + apexNamesMap()[moduleName] = apexesForModule + } + apexesForModule[apex.ApexName] = apexesForModule[apex.ApexName] || directDep } - apexNames[apexName] = apexNames[apexName] || directDep } // TODO(b/146393795): remove this when b/146393795 is fixed diff --git a/android/arch.go b/android/arch.go index 3657e6dd4..73a490d79 100644 --- a/android/arch.go +++ b/android/arch.go @@ -765,7 +765,7 @@ func osMutator(mctx BottomUpMutatorContext) { } if len(moduleOSList) == 0 { - base.commonProperties.Enabled = boolPtr(false) + base.Disable() return } @@ -869,7 +869,7 @@ func archMutator(mctx BottomUpMutatorContext) { } if len(targets) == 0 { - base.commonProperties.Enabled = boolPtr(false) + base.Disable() return } @@ -1521,12 +1521,7 @@ func decodeTargetProductVariables(config *config) (map[OsType][]Target, error) { // hasArmAbi returns true if arch has at least one arm ABI func hasArmAbi(arch Arch) bool { - for _, abi := range arch.Abi { - if strings.HasPrefix(abi, "arm") { - return true - } - } - return false + return PrefixInList(arch.Abi, "arm") } // hasArmArch returns true if targets has at least non-native_bridge arm Android arch diff --git a/android/config.go b/android/config.go index d0ac4c35d..1fe6f058b 100644 --- a/android/config.go +++ b/android/config.go @@ -790,14 +790,6 @@ func (c *config) DisableScudo() bool { return Bool(c.productVariables.DisableScudo) } -func (c *config) EnableXOM() bool { - if c.productVariables.EnableXOM == nil { - return true - } else { - return Bool(c.productVariables.EnableXOM) - } -} - func (c *config) Android64() bool { for _, t := range c.Targets[Android] { if t.Arch.ArchType.Multilib == "lib64" { @@ -898,11 +890,7 @@ func (c *config) EnforceRROForModule(name string) bool { func (c *config) EnforceRROExcludedOverlay(path string) bool { excluded := c.productVariables.EnforceRROExcludedOverlays if excluded != nil { - for _, exclude := range excluded { - if strings.HasPrefix(path, exclude) { - return true - } - } + return HasAnyPrefix(path, excluded) } return false } @@ -923,8 +911,23 @@ func (c *config) ModulesLoadedByPrivilegedModules() []string { return c.productVariables.ModulesLoadedByPrivilegedModules } +// Expected format for apexJarValue = <apex name>:<jar name> +func SplitApexJarPair(apexJarValue string) (string, string) { + var apexJarPair []string = strings.SplitN(apexJarValue, ":", 2) + if apexJarPair == nil || len(apexJarPair) != 2 { + panic(fmt.Errorf("malformed apexJarValue: %q, expected format: <apex>:<jar>", + apexJarValue)) + } + return apexJarPair[0], apexJarPair[1] +} + func (c *config) BootJars() []string { - return c.productVariables.BootJars + jars := c.productVariables.BootJars + for _, p := range c.productVariables.UpdatableBootJars { + _, jar := SplitApexJarPair(p) + jars = append(jars, jar) + } + return jars } func (c *config) DexpreoptGlobalConfig(ctx PathContext) ([]byte, error) { @@ -1043,12 +1046,12 @@ func (c *deviceConfig) ClangCoverageEnabled() bool { func (c *deviceConfig) CoverageEnabledForPath(path string) bool { coverage := false if c.config.productVariables.CoveragePaths != nil { - if InList("*", c.config.productVariables.CoveragePaths) || PrefixInList(path, c.config.productVariables.CoveragePaths) { + if InList("*", c.config.productVariables.CoveragePaths) || HasAnyPrefix(path, c.config.productVariables.CoveragePaths) { coverage = true } } if coverage && c.config.productVariables.CoverageExcludePaths != nil { - if PrefixInList(path, c.config.productVariables.CoverageExcludePaths) { + if HasAnyPrefix(path, c.config.productVariables.CoverageExcludePaths) { coverage = false } } @@ -1121,28 +1124,21 @@ func (c *config) IntegerOverflowDisabledForPath(path string) bool { if c.productVariables.IntegerOverflowExcludePaths == nil { return false } - return PrefixInList(path, c.productVariables.IntegerOverflowExcludePaths) + return HasAnyPrefix(path, c.productVariables.IntegerOverflowExcludePaths) } func (c *config) CFIDisabledForPath(path string) bool { if c.productVariables.CFIExcludePaths == nil { return false } - return PrefixInList(path, c.productVariables.CFIExcludePaths) + return HasAnyPrefix(path, c.productVariables.CFIExcludePaths) } func (c *config) CFIEnabledForPath(path string) bool { if c.productVariables.CFIIncludePaths == nil { return false } - return PrefixInList(path, c.productVariables.CFIIncludePaths) -} - -func (c *config) XOMDisabledForPath(path string) bool { - if c.productVariables.XOMExcludePaths == nil { - return false - } - return PrefixInList(path, c.productVariables.XOMExcludePaths) + return HasAnyPrefix(path, c.productVariables.CFIIncludePaths) } func (c *config) VendorConfig(name string) VendorConfig { diff --git a/android/module.go b/android/module.go index 28d83e881..fd3fec333 100644 --- a/android/module.go +++ b/android/module.go @@ -204,6 +204,7 @@ type Module interface { DepsMutator(BottomUpMutatorContext) base() *ModuleBase + Disable() Enabled() bool Target() Target InstallInData() bool @@ -293,6 +294,12 @@ type nameProperties struct { type commonProperties struct { // emit build rules for this module + // + // Disabling a module should only be done for those modules that cannot be built + // in the current environment. Modules that can build in the current environment + // but are not usually required (e.g. superceded by a prebuilt) should not be + // disabled as that will prevent them from being built by the checkbuild target + // and so prevent early detection of changes that have broken those modules. Enabled *bool `android:"arch_variant"` // Controls the visibility of this module to other modules. Allowable values are one or more of @@ -830,6 +837,10 @@ func (m *ModuleBase) Enabled() bool { return *m.commonProperties.Enabled } +func (m *ModuleBase) Disable() { + m.commonProperties.Enabled = proptools.BoolPtr(false) +} + func (m *ModuleBase) SkipInstall() { m.commonProperties.SkipInstall = true } @@ -1101,6 +1112,9 @@ func (m *ModuleBase) GenerateBuildActions(blueprintCtx blueprint.ModuleContext) if !ctx.PrimaryArch() { suffix = append(suffix, ctx.Arch().ArchType.String()) } + if apex, ok := m.module.(ApexModule); ok && !apex.IsForPlatform() { + suffix = append(suffix, apex.ApexName()) + } ctx.Variable(pctx, "moduleDesc", desc) diff --git a/android/neverallow.go b/android/neverallow.go index 48581df71..0cb20296c 100644 --- a/android/neverallow.go +++ b/android/neverallow.go @@ -407,8 +407,8 @@ func (r *rule) String() string { } func (r *rule) appliesToPath(dir string) bool { - includePath := len(r.paths) == 0 || hasAnyPrefix(dir, r.paths) - excludePath := hasAnyPrefix(dir, r.unlessPaths) + includePath := len(r.paths) == 0 || HasAnyPrefix(dir, r.paths) + excludePath := HasAnyPrefix(dir, r.unlessPaths) return includePath && !excludePath } @@ -474,15 +474,6 @@ func fieldNamesForProperties(propertyNames string) []string { return names } -func hasAnyPrefix(s string, prefixes []string) bool { - for _, prefix := range prefixes { - if strings.HasPrefix(s, prefix) { - return true - } - } - return false -} - func hasAnyProperty(properties []interface{}, props []ruleProperty) bool { for _, v := range props { if hasProperty(properties, v) { diff --git a/android/util.go b/android/util.go index e985fc1f5..ade851eff 100644 --- a/android/util.go +++ b/android/util.go @@ -122,7 +122,7 @@ func InList(s string, list []string) bool { } // Returns true if the given string s is prefixed with any string in the given prefix list. -func PrefixInList(s string, prefixList []string) bool { +func HasAnyPrefix(s string, prefixList []string) bool { for _, prefix := range prefixList { if strings.HasPrefix(s, prefix) { return true @@ -132,7 +132,7 @@ func PrefixInList(s string, prefixList []string) bool { } // Returns true if any string in the given list has the given prefix. -func PrefixedStringInList(list []string, prefix string) bool { +func PrefixInList(list []string, prefix string) bool { for _, s := range list { if strings.HasPrefix(s, prefix) { return true diff --git a/android/util_test.go b/android/util_test.go index 90fefeede..1f9ca361c 100644 --- a/android/util_test.go +++ b/android/util_test.go @@ -252,7 +252,7 @@ func TestPrefixInList(t *testing.T) { for _, testCase := range testcases { t.Run(testCase.str, func(t *testing.T) { - out := PrefixInList(testCase.str, prefixes) + out := HasAnyPrefix(testCase.str, prefixes) if out != testCase.expected { t.Errorf("incorrect output:") t.Errorf(" str: %#v", testCase.str) diff --git a/android/variable.go b/android/variable.go index 58e59404e..9cbe624e4 100644 --- a/android/variable.go +++ b/android/variable.go @@ -230,7 +230,8 @@ type productVariables struct { UncompressPrivAppDex *bool `json:",omitempty"` ModulesLoadedByPrivilegedModules []string `json:",omitempty"` - BootJars []string `json:",omitempty"` + BootJars []string `json:",omitempty"` + UpdatableBootJars []string `json:",omitempty"` IntegerOverflowExcludePaths []string `json:",omitempty"` @@ -240,9 +241,6 @@ type productVariables struct { DisableScudo *bool `json:",omitempty"` - EnableXOM *bool `json:",omitempty"` - XOMExcludePaths []string `json:",omitempty"` - Experimental_mte *bool `json:",omitempty"` VendorPath *string `json:",omitempty"` diff --git a/android/vts_config.go b/android/vts_config.go index 86f6e7281..9a1df7c99 100644 --- a/android/vts_config.go +++ b/android/vts_config.go @@ -17,6 +17,7 @@ package android import ( "fmt" "io" + "strings" ) func init() { @@ -26,6 +27,8 @@ func init() { type vtsConfigProperties struct { // Override the default (AndroidTest.xml) test manifest file name. Test_config *string + // Additional test suites to add the test to. + Test_suites []string `android:"arch_variant"` } type VtsConfig struct { @@ -50,7 +53,8 @@ func (me *VtsConfig) AndroidMk() AndroidMkData { fmt.Fprintf(w, "LOCAL_TEST_CONFIG := %s\n", *me.properties.Test_config) } - fmt.Fprintln(w, "LOCAL_COMPATIBILITY_SUITE := vts") + fmt.Fprintf(w, "LOCAL_COMPATIBILITY_SUITE := vts %s\n", + strings.Join(me.properties.Test_suites, " ")) }, } return androidMkData diff --git a/apex/apex.go b/apex/apex.go index 4fbec6026..002bf5be7 100644 --- a/apex/apex.go +++ b/apex/apex.go @@ -94,7 +94,6 @@ func makeApexAvailableWhitelist() map[string][]string { "libcutils", "libcutils_headers", "libdiagnose_usb", - "liblog", "liblog_headers", "libmdnssd", "libminijail", @@ -170,7 +169,6 @@ func makeApexAvailableWhitelist() map[string][]string { "libicuuc_headers", "libicuuc_stubdata", "libjdwp_headers", - "liblog", "liblog_headers", "liblz4", "liblzma", @@ -285,7 +283,6 @@ func makeApexAvailableWhitelist() map[string][]string { "libsystem_headers", "libtinyxml2", "libudrv-uipc", - "libutils", "libutils_headers", "libz", "media_plugin_headers", @@ -377,7 +374,6 @@ func makeApexAvailableWhitelist() map[string][]string { "libtextclassifier_hash_headers", "libtextclassifier_hash_static", "libtflite_kernel_utils", - "libutils", "libutils_headers", "philox_random", "philox_random_headers", @@ -522,7 +518,6 @@ func makeApexAvailableWhitelist() map[string][]string { "libui", "libui_headers", "libunwindstack", - "libutils", "libutils_headers", "libvibrator", "libvorbisidec", @@ -693,7 +688,6 @@ func makeApexAvailableWhitelist() map[string][]string { "libui", "libui_headers", "libunwindstack", - "libutils", "libutils_headers", "libvorbisidec", "libvpx", @@ -791,7 +785,6 @@ func makeApexAvailableWhitelist() map[string][]string { "libjemalloc5", "liblinker_main", "liblinker_malloc", - "liblog", "liblog_headers", "liblz4", "liblzma", @@ -824,7 +817,6 @@ func makeApexAvailableWhitelist() map[string][]string { "libcutils_headers", "libgtest_prod", "libjsoncpp", - "liblog", "liblog_headers", "libnativehelper_header_only", "libnetd_client_headers", @@ -840,7 +832,6 @@ func makeApexAvailableWhitelist() map[string][]string { "libstatssocket_headers", "libsystem_headers", "libsysutils", - "libutils", "libutils_headers", "netd_event_listener_interface-ndk_platform", "server_configurable_flags", @@ -866,13 +857,11 @@ func makeApexAvailableWhitelist() map[string][]string { "libhidltransport-impl-internal", "libhwbinder-impl-internal", "libjsoncpp", - "liblog", "liblog_headers", "libprocessgroup", "libprocessgroup_headers", "libsystem_headers", "libtetherutilsjni", - "libutils", "libutils_headers", "libvndksupport", "tethering-aidl-interfaces-java", @@ -919,7 +908,6 @@ func makeApexAvailableWhitelist() map[string][]string { "libprotobuf-java-lite", "libprotobuf-java-nano", "libsystem_headers", - "libutils", "libutils_headers", "libwifi-jni", "net-utils-services-common", @@ -1027,7 +1015,7 @@ func RegisterPreDepsMutators(ctx android.RegisterMutatorsContext) { } func RegisterPostDepsMutators(ctx android.RegisterMutatorsContext) { - ctx.BottomUp("apex_deps", apexDepsMutator) + ctx.TopDown("apex_deps", apexDepsMutator) ctx.BottomUp("apex", apexMutator).Parallel() ctx.BottomUp("apex_flattened", apexFlattenedMutator).Parallel() ctx.BottomUp("apex_uses", apexUsesMutator).Parallel() @@ -1035,24 +1023,29 @@ func RegisterPostDepsMutators(ctx android.RegisterMutatorsContext) { // Mark the direct and transitive dependencies of apex bundles so that they // can be built for the apex bundles. -func apexDepsMutator(mctx android.BottomUpMutatorContext) { +func apexDepsMutator(mctx android.TopDownMutatorContext) { + var apexBundles []android.ApexInfo + var directDep bool if a, ok := mctx.Module().(*apexBundle); ok && !a.vndkApex { - apexBundleName := mctx.ModuleName() - mctx.WalkDeps(func(child, parent android.Module) bool { - depName := mctx.OtherModuleName(child) - // If the parent is apexBundle, this child is directly depended. - _, directDep := parent.(*apexBundle) - android.UpdateApexDependency(apexBundleName, depName, directDep) - - if am, ok := child.(android.ApexModule); ok && am.CanHaveApexVariants() && - (directDep || am.DepIsInSameApex(mctx, child)) { - am.BuildForApex(apexBundleName) - return true - } else { - return false - } - }) + apexBundles = []android.ApexInfo{{mctx.ModuleName(), proptools.Bool(a.properties.Legacy_android10_support)}} + directDep = true + } else if am, ok := mctx.Module().(android.ApexModule); ok { + apexBundles = am.ApexVariations() + directDep = false } + + if len(apexBundles) == 0 { + return + } + + mctx.VisitDirectDeps(func(child android.Module) { + depName := mctx.OtherModuleName(child) + if am, ok := child.(android.ApexModule); ok && am.CanHaveApexVariants() && + (directDep || am.DepIsInSameApex(mctx, child)) { + android.UpdateApexDependency(apexBundles, depName, directDep) + am.BuildForApexes(apexBundles) + } + }) } // Create apex variations if a module is included in APEX(s). diff --git a/apex/apex_test.go b/apex/apex_test.go index 0420586de..5000c8803 100644 --- a/apex/apex_test.go +++ b/apex/apex_test.go @@ -3437,6 +3437,7 @@ func TestLegacyAndroid10Support(t *testing.T) { apex { name: "myapex", key: "myapex.key", + native_shared_libs: ["mylib"], legacy_android10_support: true, } @@ -3445,12 +3446,54 @@ func TestLegacyAndroid10Support(t *testing.T) { public_key: "testkey.avbpubkey", private_key: "testkey.pem", } - `) + + cc_library { + name: "mylib", + srcs: ["mylib.cpp"], + stl: "libc++", + system_shared_libs: [], + apex_available: [ "myapex" ], + } + + cc_library { + name: "libc++", + srcs: ["mylib.cpp"], + stl: "none", + system_shared_libs: [], + apex_available: [ "myapex" ], + } + + cc_library_static { + name: "libc++demangle", + srcs: ["mylib.cpp"], + stl: "none", + system_shared_libs: [], + } + + cc_library_static { + name: "libunwind_llvm", + srcs: ["mylib.cpp"], + stl: "none", + system_shared_libs: [], + } + `, withUnbundledBuild) module := ctx.ModuleForTests("myapex", "android_common_myapex_image") args := module.Rule("apexRule").Args ensureContains(t, args["opt_flags"], "--manifest_json "+module.Output("apex_manifest.json").Output.String()) ensureNotContains(t, args["opt_flags"], "--no_hashtree") + + // The copies of the libraries in the apex should have one more dependency than + // the ones outside the apex, namely the unwinder. Ideally we should check + // the dependency names directly here but for some reason the names are blank in + // this test. + for _, lib := range []string{"libc++", "mylib"} { + apexImplicits := ctx.ModuleForTests(lib, "android_arm64_armv8-a_shared_myapex").Rule("ld").Implicits + nonApexImplicits := ctx.ModuleForTests(lib, "android_arm64_armv8-a_shared").Rule("ld").Implicits + if len(apexImplicits) != len(nonApexImplicits)+1 { + t.Errorf("%q missing unwinder dep", lib) + } + } } func TestJavaSDKLibrary(t *testing.T) { diff --git a/cc/androidmk.go b/cc/androidmk.go index 137cb63d7..d8210fc9b 100644 --- a/cc/androidmk.go +++ b/cc/androidmk.go @@ -413,9 +413,12 @@ func (c *llndkStubDecorator) AndroidMk(ctx AndroidMkContext, ret *android.Androi } func (c *vndkPrebuiltLibraryDecorator) AndroidMk(ctx AndroidMkContext, ret *android.AndroidMkData) { + // Each vndk prebuilt is exported to androidMk only when BOARD_VNDK_VERSION != current + // and the version of the prebuilt is same as BOARD_VNDK_VERSION. ret.Class = "SHARED_LIBRARIES" - ret.SubName = c.NameSuffix() + // shouldn't add any suffixes due to mk modules + ret.SubName = "" ret.Extra = append(ret.Extra, func(w io.Writer, outputFile android.Path) { c.libraryDecorator.androidMkWriteExportedFlags(w) @@ -426,6 +429,61 @@ func (c *vndkPrebuiltLibraryDecorator) AndroidMk(ctx AndroidMkContext, ret *andr fmt.Fprintln(w, "LOCAL_MODULE_SUFFIX := "+suffix) fmt.Fprintln(w, "LOCAL_MODULE_PATH := "+path) fmt.Fprintln(w, "LOCAL_MODULE_STEM := "+stem) + if c.tocFile.Valid() { + fmt.Fprintln(w, "LOCAL_SOONG_TOC := "+c.tocFile.String()) + } + }) +} + +func (c *vendorSnapshotLibraryDecorator) AndroidMk(ctx AndroidMkContext, ret *android.AndroidMkData) { + // Each vendor snapshot is exported to androidMk only when BOARD_VNDK_VERSION != current + // and the version of the prebuilt is same as BOARD_VNDK_VERSION. + if c.shared() { + ret.Class = "SHARED_LIBRARIES" + } else if c.static() { + ret.Class = "STATIC_LIBRARIES" + } else if c.header() { + ret.Class = "HEADER_LIBRARIES" + } + + if c.androidMkVendorSuffix { + ret.SubName = vendorSuffix + } else { + ret.SubName = "" + } + + ret.Extra = append(ret.Extra, func(w io.Writer, outputFile android.Path) { + c.libraryDecorator.androidMkWriteExportedFlags(w) + + if c.shared() { + path, file := filepath.Split(c.path.ToMakePath().String()) + stem, suffix, ext := android.SplitFileExt(file) + fmt.Fprintln(w, "LOCAL_BUILT_MODULE_STEM := $(LOCAL_MODULE)"+ext) + fmt.Fprintln(w, "LOCAL_MODULE_SUFFIX := "+suffix) + if c.shared() { + fmt.Fprintln(w, "LOCAL_MODULE_PATH := "+path) + fmt.Fprintln(w, "LOCAL_MODULE_STEM := "+stem) + } + if c.tocFile.Valid() { + fmt.Fprintln(w, "LOCAL_SOONG_TOC := "+c.tocFile.String()) + } + } else { // static or header + fmt.Fprintln(w, "LOCAL_UNINSTALLABLE_MODULE := true") + } + }) +} + +func (c *vendorSnapshotBinaryDecorator) AndroidMk(ctx AndroidMkContext, ret *android.AndroidMkData) { + ret.Class = "EXECUTABLES" + + if c.androidMkVendorSuffix { + ret.SubName = vendorSuffix + } else { + ret.SubName = "" + } + + ret.Extra = append(ret.Extra, func(w io.Writer, outputFile android.Path) { + fmt.Fprintln(w, "LOCAL_MODULE_SYMLINKS := "+strings.Join(c.Properties.Symlinks, " ")) }) } diff --git a/cc/binary.go b/cc/binary.go index 280d17bfb..661264eef 100644 --- a/cc/binary.go +++ b/cc/binary.go @@ -200,6 +200,11 @@ func NewBinary(hod android.HostOrDeviceSupported) (*Module, *binaryDecorator) { module.compiler = NewBaseCompiler() module.linker = binary module.installer = binary + + // Allow module to be added as member of an sdk/module_exports. + module.sdkMemberTypes = []android.SdkMemberType{ + ccBinarySdkMemberType, + } return module, binary } diff --git a/cc/binary_sdk_member.go b/cc/binary_sdk_member.go new file mode 100644 index 000000000..53bc065f9 --- /dev/null +++ b/cc/binary_sdk_member.go @@ -0,0 +1,143 @@ +// Copyright 2020 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 cc + +import ( + "path/filepath" + + "android/soong/android" + "github.com/google/blueprint" +) + +func init() { + android.RegisterSdkMemberType(ccBinarySdkMemberType) +} + +var ccBinarySdkMemberType = &binarySdkMemberType{ + SdkMemberTypeBase: android.SdkMemberTypeBase{ + PropertyName: "native_binaries", + }, +} + +type binarySdkMemberType struct { + android.SdkMemberTypeBase +} + +func (mt *binarySdkMemberType) AddDependencies(mctx android.BottomUpMutatorContext, dependencyTag blueprint.DependencyTag, names []string) { + targets := mctx.MultiTargets() + for _, lib := range names { + for _, target := range targets { + name, version := StubsLibNameAndVersion(lib) + if version == "" { + version = LatestStubsVersionFor(mctx.Config(), name) + } + mctx.AddFarVariationDependencies(append(target.Variations(), []blueprint.Variation{ + {Mutator: "version", Variation: version}, + }...), dependencyTag, name) + } + } +} + +func (mt *binarySdkMemberType) IsInstance(module android.Module) bool { + // Check the module to see if it can be used with this module type. + if m, ok := module.(*Module); ok { + for _, allowableMemberType := range m.sdkMemberTypes { + if allowableMemberType == mt { + return true + } + } + } + + return false +} + +func (mt *binarySdkMemberType) BuildSnapshot(sdkModuleContext android.ModuleContext, builder android.SnapshotBuilder, member android.SdkMember) { + info := mt.organizeVariants(member) + buildSharedNativeBinarySnapshot(info, builder, member) +} + +// Organize the variants by architecture. +func (mt *binarySdkMemberType) organizeVariants(member android.SdkMember) *nativeBinaryInfo { + memberName := member.Name() + info := &nativeBinaryInfo{ + name: memberName, + memberType: mt, + } + + for _, variant := range member.Variants() { + ccModule := variant.(*Module) + + info.archVariantProperties = append(info.archVariantProperties, nativeBinaryInfoProperties{ + name: memberName, + archType: ccModule.Target().Arch.ArchType.String(), + outputFile: ccModule.OutputFile().Path(), + }) + } + + // Initialize the unexported properties that will not be set during the + // extraction process. + info.commonProperties.name = memberName + + // Extract common properties from the arch specific properties. + extractCommonProperties(&info.commonProperties, info.archVariantProperties) + + return info +} + +func buildSharedNativeBinarySnapshot(info *nativeBinaryInfo, builder android.SnapshotBuilder, member android.SdkMember) { + pbm := builder.AddPrebuiltModule(member, "cc_prebuilt_binary") + pbm.AddProperty("compile_multilib", "both") + archProperties := pbm.AddPropertySet("arch") + for _, av := range info.archVariantProperties { + archTypeProperties := archProperties.AddPropertySet(av.archType) + archTypeProperties.AddProperty("srcs", []string{nativeBinaryPathFor(av)}) + + builder.CopyToSnapshot(av.outputFile, nativeBinaryPathFor(av)) + } +} + +const ( + nativeBinaryDir = "bin" +) + +// path to the native binary. Relative to <sdk_root>/<api_dir> +func nativeBinaryPathFor(lib nativeBinaryInfoProperties) string { + return filepath.Join(lib.archType, + nativeBinaryDir, lib.outputFile.Base()) +} + +// nativeBinaryInfoProperties represents properties of a native binary +// +// The exported (capitalized) fields will be examined and may be changed during common value extraction. +// The unexported fields will be left untouched. +type nativeBinaryInfoProperties struct { + // The name of the library, is not exported as this must not be changed during optimization. + name string + + // archType is not exported as if set (to a non default value) it is always arch specific. + // This is "" for common properties. + archType string + + // outputFile is not exported as it is always arch specific. + outputFile android.Path +} + +// nativeBinaryInfo represents a collection of arch-specific modules having the same name +type nativeBinaryInfo struct { + name string + memberType *binarySdkMemberType + archVariantProperties []nativeBinaryInfoProperties + commonProperties nativeBinaryInfoProperties +} diff --git a/cc/builder.go b/cc/builder.go index 3ecfe540c..136263b41 100644 --- a/cc/builder.go +++ b/cc/builder.go @@ -55,9 +55,9 @@ var ( }, "ccCmd", "cFlags") - ccNoDeps = pctx.AndroidRemoteStaticRule("ccNoDeps", android.RemoteRuleSupports{Goma: true}, + ccNoDeps = pctx.AndroidStaticRule("ccNoDeps", blueprint.RuleParams{ - Command: "$relPwd ${config.CcWrapper}$ccCmd -c $cFlags -o $out $in", + Command: "$relPwd $ccCmd -c $cFlags -o $out $in", CommandDeps: []string{"$ccCmd"}, }, "ccCmd", "cFlags") @@ -49,6 +49,8 @@ func RegisterCCBuildComponents(ctx android.RegistrationContext) { ctx.BottomUp("version", VersionMutator).Parallel() ctx.BottomUp("begin", BeginMutator).Parallel() ctx.BottomUp("sysprop_cc", SyspropMutator).Parallel() + ctx.BottomUp("vendor_snapshot", VendorSnapshotMutator).Parallel() + ctx.BottomUp("vendor_snapshot_source", VendorSnapshotSourceMutator).Parallel() }) ctx.PostDepsMutators(func(ctx android.RegisterMutatorsContext) { @@ -93,6 +95,8 @@ type Deps struct { HeaderLibs []string RuntimeLibs []string + StaticUnwinderIfLegacy bool + ReexportSharedLibHeaders, ReexportStaticLibHeaders, ReexportHeaderLibHeaders []string ObjFiles []string @@ -383,6 +387,7 @@ var ( lateSharedDepTag = DependencyTag{Name: "late shared", Library: true, Shared: true} staticExportDepTag = DependencyTag{Name: "static", Library: true, ReexportFlags: true} lateStaticDepTag = DependencyTag{Name: "late static", Library: true} + staticUnwinderDepTag = DependencyTag{Name: "static unwinder", Library: true} wholeStaticDepTag = DependencyTag{Name: "whole static", Library: true, ReexportFlags: true} headerDepTag = DependencyTag{Name: "header", Library: true} headerExportDepTag = DependencyTag{Name: "header", Library: true, ReexportFlags: true} @@ -448,7 +453,6 @@ type Module struct { vndkdep *vndkdep lto *lto pgo *pgo - xom *xom outputFile android.OptionalPath @@ -732,9 +736,6 @@ func (c *Module) Init() android.Module { if c.pgo != nil { c.AddProperties(c.pgo.props()...) } - if c.xom != nil { - c.AddProperties(c.xom.props()...) - } for _, feature := range c.features { c.AddProperties(feature.props()...) } @@ -923,8 +924,16 @@ func (c *Module) nativeCoverage() bool { } func (c *Module) isSnapshotPrebuilt() bool { - _, ok := c.linker.(*vndkPrebuiltLibraryDecorator) - return ok + if _, ok := c.linker.(*vndkPrebuiltLibraryDecorator); ok { + return true + } + if _, ok := c.linker.(*vendorSnapshotLibraryDecorator); ok { + return true + } + if _, ok := c.linker.(*vendorSnapshotBinaryDecorator); ok { + return true + } + return false } func (c *Module) ExportedIncludeDirs() android.Paths { @@ -1196,7 +1205,6 @@ func newModule(hod android.HostOrDeviceSupported, multilib android.Multilib) *Mo module.vndkdep = &vndkdep{} module.lto = <o{} module.pgo = &pgo{} - module.xom = &xom{} return module } @@ -1391,9 +1399,6 @@ func (c *Module) GenerateAndroidBuildActions(actx android.ModuleContext) { if c.pgo != nil { flags = c.pgo.flags(ctx, flags) } - if c.xom != nil { - flags = c.xom.flags(ctx, flags) - } for _, feature := range c.features { flags = feature.flags(ctx, flags) } @@ -1612,6 +1617,10 @@ func StubsLibNameAndVersion(name string) (string, string) { } func (c *Module) DepsMutator(actx android.BottomUpMutatorContext) { + if !c.Enabled() { + return + } + ctx := &depsContext{ BottomUpMutatorContext: actx, moduleContextImpl: moduleContextImpl{ @@ -1627,7 +1636,7 @@ func (c *Module) DepsMutator(actx android.BottomUpMutatorContext) { if ctx.Os() == android.Android { version := ctx.sdkVersion() - // rewriteNdkLibs takes a list of names of shared libraries and scans it for three types + // rewriteLibs takes a list of names of shared libraries and scans it for three types // of names: // // 1. Name of an NDK library that refers to a prebuilt module. @@ -1643,7 +1652,26 @@ func (c *Module) DepsMutator(actx android.BottomUpMutatorContext) { // nonvariantLibs vendorPublicLibraries := vendorPublicLibraries(actx.Config()) - rewriteNdkLibs := func(list []string) (nonvariantLibs []string, variantLibs []string) { + vendorSnapshotSharedLibs := vendorSnapshotSharedLibs(actx.Config()) + + rewriteVendorLibs := func(lib string) string { + if isLlndkLibrary(lib, ctx.Config()) { + return lib + llndkLibrarySuffix + } + + // only modules with BOARD_VNDK_VERSION uses snapshot. + if c.VndkVersion() != actx.DeviceConfig().VndkVersion() { + return lib + } + + if snapshot, ok := vendorSnapshotSharedLibs.get(lib, actx.Arch().ArchType); ok { + return snapshot + } + + return lib + } + + rewriteLibs := func(list []string) (nonvariantLibs []string, variantLibs []string) { variantLibs = []string{} nonvariantLibs = []string{} for _, entry := range list { @@ -1655,8 +1683,8 @@ func (c *Module) DepsMutator(actx android.BottomUpMutatorContext) { } else { variantLibs = append(variantLibs, name+ndkLibrarySuffix) } - } else if ctx.useVndk() && isLlndkLibrary(name, ctx.Config()) { - nonvariantLibs = append(nonvariantLibs, name+llndkLibrarySuffix) + } else if ctx.useVndk() { + nonvariantLibs = append(nonvariantLibs, rewriteVendorLibs(entry)) } else if (ctx.Platform() || ctx.ProductSpecific()) && inList(name, *vendorPublicLibraries) { vendorPublicLib := name + vendorPublicLibrarySuffix if actx.OtherModuleExists(vendorPublicLib) { @@ -1675,9 +1703,14 @@ func (c *Module) DepsMutator(actx android.BottomUpMutatorContext) { return nonvariantLibs, variantLibs } - deps.SharedLibs, variantNdkLibs = rewriteNdkLibs(deps.SharedLibs) - deps.LateSharedLibs, variantLateNdkLibs = rewriteNdkLibs(deps.LateSharedLibs) - deps.ReexportSharedLibHeaders, _ = rewriteNdkLibs(deps.ReexportSharedLibHeaders) + deps.SharedLibs, variantNdkLibs = rewriteLibs(deps.SharedLibs) + deps.LateSharedLibs, variantLateNdkLibs = rewriteLibs(deps.LateSharedLibs) + deps.ReexportSharedLibHeaders, _ = rewriteLibs(deps.ReexportSharedLibHeaders) + if ctx.useVndk() { + for idx, lib := range deps.RuntimeLibs { + deps.RuntimeLibs[idx] = rewriteVendorLibs(lib) + } + } } buildStubs := false @@ -1689,11 +1722,28 @@ func (c *Module) DepsMutator(actx android.BottomUpMutatorContext) { } } + rewriteSnapshotLibs := func(lib string, snapshotMap *snapshotMap) string { + // only modules with BOARD_VNDK_VERSION uses snapshot. + if c.VndkVersion() != actx.DeviceConfig().VndkVersion() { + return lib + } + + if snapshot, ok := snapshotMap.get(lib, actx.Arch().ArchType); ok { + return snapshot + } + + return lib + } + + vendorSnapshotHeaderLibs := vendorSnapshotHeaderLibs(actx.Config()) for _, lib := range deps.HeaderLibs { depTag := headerDepTag if inList(lib, deps.ReexportHeaderLibHeaders) { depTag = headerExportDepTag } + + lib = rewriteSnapshotLibs(lib, vendorSnapshotHeaderLibs) + if buildStubs { actx.AddFarVariationDependencies(append(ctx.Target().Variations(), c.ImageVariation()), depTag, lib) @@ -1709,12 +1759,16 @@ func (c *Module) DepsMutator(actx android.BottomUpMutatorContext) { } syspropImplLibraries := syspropImplLibraries(actx.Config()) + vendorSnapshotStaticLibs := vendorSnapshotStaticLibs(actx.Config()) for _, lib := range deps.WholeStaticLibs { depTag := wholeStaticDepTag if impl, ok := syspropImplLibraries[lib]; ok { lib = impl } + + lib = rewriteSnapshotLibs(lib, vendorSnapshotStaticLibs) + actx.AddVariationDependencies([]blueprint.Variation{ {Mutator: "link", Variation: "static"}, }, depTag, lib) @@ -1730,14 +1784,24 @@ func (c *Module) DepsMutator(actx android.BottomUpMutatorContext) { lib = impl } + lib = rewriteSnapshotLibs(lib, vendorSnapshotStaticLibs) + actx.AddVariationDependencies([]blueprint.Variation{ {Mutator: "link", Variation: "static"}, }, depTag, lib) } - actx.AddVariationDependencies([]blueprint.Variation{ - {Mutator: "link", Variation: "static"}, - }, lateStaticDepTag, deps.LateStaticLibs...) + if deps.StaticUnwinderIfLegacy && ctx.Config().UnbundledBuild() { + actx.AddVariationDependencies([]blueprint.Variation{ + {Mutator: "link", Variation: "static"}, + }, staticUnwinderDepTag, staticUnwinder(actx)) + } + + for _, lib := range deps.LateStaticLibs { + actx.AddVariationDependencies([]blueprint.Variation{ + {Mutator: "link", Variation: "static"}, + }, lateStaticDepTag, rewriteSnapshotLibs(lib, vendorSnapshotStaticLibs)) + } addSharedLibDependencies := func(depTag DependencyTag, name string, version string) { var variations []blueprint.Variation @@ -2106,6 +2170,14 @@ func (c *Module) depsToPaths(ctx android.ModuleContext) PathDeps { } } + if depTag == staticUnwinderDepTag { + if c.ApexProperties.Info.LegacyAndroid10Support { + depTag = StaticDepTag + } else { + 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. @@ -2284,14 +2356,33 @@ func (c *Module) depsToPaths(ctx android.ModuleContext) PathDeps { *depPtr = append(*depPtr, dep.Path()) } - makeLibName := func(depName string) string { + vendorSuffixModules := vendorSuffixModules(ctx.Config()) + + baseLibName := func(depName string) string { libName := strings.TrimSuffix(depName, llndkLibrarySuffix) libName = strings.TrimSuffix(libName, vendorPublicLibrarySuffix) libName = strings.TrimPrefix(libName, "prebuilt_") + return libName + } + + makeLibName := func(depName string) string { + libName := baseLibName(depName) isLLndk := isLlndkLibrary(libName, ctx.Config()) isVendorPublicLib := inList(libName, *vendorPublicLibraries) bothVendorAndCoreVariantsExist := ccDep.HasVendorVariant() || isLLndk + if c, ok := ccDep.(*Module); ok { + // Use base module name for snapshots when exporting to Makefile. + if c.isSnapshotPrebuilt() && !c.IsVndk() { + baseName := c.BaseModuleName() + if vendorSuffixModules[baseName] { + return baseName + ".vendor" + } else { + return baseName + } + } + } + if ctx.DeviceConfig().VndkUseCoreVariant() && ccDep.IsVndk() && !ccDep.MustUseVendorVariant() && !c.InRamdisk() && !c.InRecovery() { // The vendor module is a no-vendor-variant VNDK library. Depend on the // core module instead. @@ -2331,8 +2422,8 @@ func (c *Module) depsToPaths(ctx android.ModuleContext) PathDeps { // they merely serve as Make dependencies and do not affect this lib itself. c.Properties.AndroidMkSharedLibs = append( c.Properties.AndroidMkSharedLibs, makeLibName(depName)) - // Record depName as-is for snapshots. - c.Properties.SnapshotSharedLibs = append(c.Properties.SnapshotSharedLibs, depName) + // Record baseLibName for snapshots. + c.Properties.SnapshotSharedLibs = append(c.Properties.SnapshotSharedLibs, baseLibName(depName)) case ndkStubDepTag, ndkLateStubDepTag: c.Properties.AndroidMkSharedLibs = append( c.Properties.AndroidMkSharedLibs, @@ -2343,8 +2434,8 @@ func (c *Module) depsToPaths(ctx android.ModuleContext) PathDeps { case runtimeDepTag: c.Properties.AndroidMkRuntimeLibs = append( c.Properties.AndroidMkRuntimeLibs, makeLibName(depName)) - // Record depName as-is for snapshots. - c.Properties.SnapshotRuntimeLibs = append(c.Properties.SnapshotRuntimeLibs, depName) + // Record baseLibName for snapshots. + c.Properties.SnapshotRuntimeLibs = append(c.Properties.SnapshotRuntimeLibs, baseLibName(depName)) case wholeStaticDepTag: c.Properties.AndroidMkWholeStaticLibs = append( c.Properties.AndroidMkWholeStaticLibs, makeLibName(depName)) @@ -2601,7 +2692,6 @@ func DefaultsFactory(props ...interface{}) android.Module { &VndkProperties{}, <OProperties{}, &PgoProperties{}, - &XomProperties{}, &android.ProtoProperties{}, ) @@ -2719,10 +2809,16 @@ func (m *Module) ImageMutatorBegin(mctx android.BaseModuleContext) { platformVndkVersion, productVndkVersion, ) - } else if lib, ok := m.linker.(*vndkPrebuiltLibraryDecorator); ok { + } else if m.isSnapshotPrebuilt() { // Make vendor variants only for the versions in BOARD_VNDK_VERSION and // PRODUCT_EXTRA_VNDK_VERSIONS. - vendorVariants = append(vendorVariants, lib.version()) + if snapshot, ok := m.linker.(interface { + version() string + }); ok { + vendorVariants = append(vendorVariants, snapshot.version()) + } else { + mctx.ModuleErrorf("version is unknown for snapshot prebuilt") + } } else if m.HasVendorVariant() && !vendorSpecific { // This will be available in /system, /vendor and /product // or a /system directory that is available to vendor and product. @@ -2819,6 +2915,14 @@ func (c *Module) SetImageVariation(ctx android.BaseModuleContext, variant string m.Properties.ImageVariationPrefix = VendorVariationPrefix m.Properties.VndkVersion = strings.TrimPrefix(variant, VendorVariationPrefix) squashVendorSrcs(m) + + // Makefile shouldn't know vendor modules other than BOARD_VNDK_VERSION. + // Hide other vendor variants to avoid collision. + vndkVersion := ctx.DeviceConfig().VndkVersion() + if vndkVersion != "current" && vndkVersion != "" && vndkVersion != m.Properties.VndkVersion { + m.Properties.HideFromMake = true + m.SkipInstall() + } } else if strings.HasPrefix(variant, ProductVariationPrefix) { m.Properties.ImageVariationPrefix = ProductVariationPrefix m.Properties.VndkVersion = strings.TrimPrefix(variant, ProductVariationPrefix) diff --git a/cc/cflag_artifacts.go b/cc/cflag_artifacts.go index b61f2a8be..855ff25b3 100644 --- a/cc/cflag_artifacts.go +++ b/cc/cflag_artifacts.go @@ -41,12 +41,7 @@ type cflagArtifactsText struct { // filter. func allowedDir(subdir string) bool { subdir += "/" - for _, prefix := range TrackedCFlagsDir { - if strings.HasPrefix(subdir, prefix) { - return true - } - } - return false + return android.HasAnyPrefix(subdir, TrackedCFlagsDir) } func (s *cflagArtifactsText) genFlagFilename(flag string) string { diff --git a/cc/compiler.go b/cc/compiler.go index 1ced451fa..c1a8d96d8 100644 --- a/cc/compiler.go +++ b/cc/compiler.go @@ -241,12 +241,7 @@ func (compiler *baseCompiler) compilerDeps(ctx DepsContext, deps Deps) Deps { // Return true if the module is in the WarningAllowedProjects. func warningsAreAllowed(subdir string) bool { subdir += "/" - for _, prefix := range config.WarningAllowedProjects { - if strings.HasPrefix(subdir, prefix) { - return true - } - } - return false + return android.HasAnyPrefix(subdir, config.WarningAllowedProjects) } func addToModuleList(ctx ModuleContext, key android.OnceKey, module string) { @@ -515,7 +510,7 @@ func (compiler *baseCompiler) compilerFlags(ctx ModuleContext, flags Flags, deps // Exclude directories from manual binder interface whitelisting. //TODO(b/145621474): Move this check into IInterface.h when clang-tidy no longer uses absolute paths. - if android.PrefixInList(ctx.ModuleDir(), allowedManualInterfacePaths) { + if android.HasAnyPrefix(ctx.ModuleDir(), allowedManualInterfacePaths) { flags.Local.CFlags = append(flags.Local.CFlags, "-DDO_NOT_CHECK_MANUAL_BINDER_INTERFACES") } @@ -604,16 +599,12 @@ var thirdPartyDirPrefixExceptions = []*regexp.Regexp{ func isThirdParty(path string) bool { thirdPartyDirPrefixes := []string{"external/", "vendor/", "hardware/"} - for _, prefix := range thirdPartyDirPrefixes { - if strings.HasPrefix(path, prefix) { - for _, prefix := range thirdPartyDirPrefixExceptions { - if prefix.MatchString(path) { - return false - } + if android.HasAnyPrefix(path, thirdPartyDirPrefixes) { + for _, prefix := range thirdPartyDirPrefixExceptions { + if prefix.MatchString(path) { + return false } - break } } - return true } diff --git a/cc/config/clang.go b/cc/config/clang.go index 8618d0955..0d036999f 100644 --- a/cc/config/clang.go +++ b/cc/config/clang.go @@ -169,6 +169,11 @@ func init() { "-Wno-reorder-init-list", // http://b/145211066 "-Wno-implicit-int-float-conversion", + // New warnings to be fixed after clang-r377782. + "-Wno-bitwise-conditional-parentheses", // http://b/148286937 + "-Wno-int-in-bool-context", // http://b/148287349 + "-Wno-sizeof-array-div", // http://b/148815709 + "-Wno-tautological-overlap-compare", // http://b/148815696 }, " ")) // Extra cflags for external third-party projects to disable warnings that diff --git a/cc/config/global.go b/cc/config/global.go index 333885f14..d01dd843e 100644 --- a/cc/config/global.go +++ b/cc/config/global.go @@ -127,8 +127,8 @@ var ( // prebuilts/clang default settings. ClangDefaultBase = "prebuilts/clang/host" - ClangDefaultVersion = "clang-r370808b" - ClangDefaultShortVersion = "10.0.2" + ClangDefaultVersion = "clang-r377782b" + ClangDefaultShortVersion = "10.0.4" // Directories with warnings from Android.bp files. WarningAllowedProjects = []string{ diff --git a/cc/config/x86_darwin_host.go b/cc/config/x86_darwin_host.go index 63b9d4872..25225b5cb 100644 --- a/cc/config/x86_darwin_host.go +++ b/cc/config/x86_darwin_host.go @@ -100,7 +100,7 @@ func init() { pctx.VariableFunc("macSdkRoot", func(ctx android.PackageVarContext) string { return xcrunSdk(ctx, "--show-sdk-path") }) - pctx.StaticVariable("macMinVersion", "10.8") + pctx.StaticVariable("macMinVersion", "10.10") pctx.VariableFunc("MacArPath", func(ctx android.PackageVarContext) string { return xcrun(ctx, "--find", "ar") }) diff --git a/cc/library.go b/cc/library.go index 0bddab534..bca9a9680 100644 --- a/cc/library.go +++ b/cc/library.go @@ -281,11 +281,9 @@ func (f *flagExporter) reexportSystemDirs(dirs ...android.Path) { } func (f *flagExporter) reexportFlags(flags ...string) { - for _, flag := range flags { - if strings.HasPrefix(flag, "-I") || strings.HasPrefix(flag, "-isystem") { - panic(fmt.Errorf("Exporting invalid flag %q: "+ - "use reexportDirs or reexportSystemDirs to export directories", flag)) - } + if android.PrefixInList(flags, "-I") || android.PrefixInList(flags, "-isystem") { + panic(fmt.Errorf("Exporting invalid flag %q: "+ + "use reexportDirs or reexportSystemDirs to export directories", flag)) } f.flags = append(f.flags, flags...) } @@ -256,6 +256,12 @@ func (pgo *pgo) begin(ctx BaseModuleContext) { } } + // PGO profile use is not feasible for a Clang coverage build because + // -fprofile-use and -fprofile-instr-generate are incompatible. + if ctx.DeviceConfig().ClangCoverageEnabled() { + return + } + if !ctx.Config().IsEnvTrue("ANDROID_PGO_NO_PROFILE_USE") && proptools.BoolDefault(pgo.Properties.Pgo.Enable_profile_use, true) { if profileFile := pgo.Properties.getPgoProfileFile(ctx); profileFile.Valid() { diff --git a/cc/sanitize.go b/cc/sanitize.go index 6f9dbef63..5663aa7ae 100644 --- a/cc/sanitize.go +++ b/cc/sanitize.go @@ -350,6 +350,12 @@ func (sanitize *sanitize) begin(ctx BaseModuleContext) { s.Diag.Cfi = nil } + // Also disable CFI if building against snapshot. + vndkVersion := ctx.DeviceConfig().VndkVersion() + if ctx.useVndk() && vndkVersion != "current" && vndkVersion != "" { + s.Cfi = nil + } + // HWASan ramdisk (which is built from recovery) goes over some bootloader limit. // Keep libc instrumented so that ramdisk / recovery can run hwasan-instrumented code if necessary. if (ctx.inRamdisk() || ctx.inRecovery()) && !strings.HasPrefix(ctx.ModuleDir(), "bionic/libc") { @@ -739,7 +745,11 @@ func sanitizerRuntimeDepsMutator(mctx android.TopDownMutatorContext) { return false } - if d, ok := child.(*Module); ok && d.static() && d.sanitize != nil { + d, ok := child.(*Module) + if !ok || !d.static() { + return false + } + if d.sanitize != nil { if enableMinimalRuntime(d.sanitize) { // If a static dependency is built with the minimal runtime, // make sure we include the ubsan minimal runtime. @@ -757,9 +767,18 @@ func sanitizerRuntimeDepsMutator(mctx android.TopDownMutatorContext) { } return true - } else { - return false } + + if p, ok := d.linker.(*vendorSnapshotLibraryDecorator); ok { + if Bool(p.properties.Sanitize_minimal_dep) { + c.sanitize.Properties.MinimalRuntimeDep = true + } + if Bool(p.properties.Sanitize_ubsan_dep) { + c.sanitize.Properties.UbsanRuntimeDep = true + } + } + + return false }) } } @@ -900,12 +919,31 @@ func sanitizerRuntimeMutator(mctx android.BottomUpMutatorContext) { // Note that by adding dependency with {static|shared}DepTag, the lib is // added to libFlags and LOCAL_SHARED_LIBRARIES by cc.Module if c.staticBinary() { + deps := append(extraStaticDeps, runtimeLibrary) + // If we're using snapshots and in vendor, redirect to snapshot whenever possible + if c.VndkVersion() == mctx.DeviceConfig().VndkVersion() { + snapshots := vendorSnapshotStaticLibs(mctx.Config()) + for idx, dep := range deps { + if lib, ok := snapshots.get(dep, mctx.Arch().ArchType); ok { + deps[idx] = lib + } + } + } + // static executable gets static runtime libs mctx.AddFarVariationDependencies(append(mctx.Target().Variations(), []blueprint.Variation{ {Mutator: "link", Variation: "static"}, c.ImageVariation(), - }...), StaticDepTag, append([]string{runtimeLibrary}, extraStaticDeps...)...) + }...), StaticDepTag, deps...) } else if !c.static() && !c.header() { + // If we're using snapshots and in vendor, redirect to snapshot whenever possible + if c.VndkVersion() == mctx.DeviceConfig().VndkVersion() { + snapshots := vendorSnapshotSharedLibs(mctx.Config()) + if lib, ok := snapshots.get(runtimeLibrary, mctx.Arch().ArchType); ok { + runtimeLibrary = lib + } + } + // dynamic executable and shared libs get shared runtime libs mctx.AddFarVariationDependencies(append(mctx.Target().Variations(), []blueprint.Variation{ {Mutator: "link", Variation: "shared"}, diff --git a/cc/snapshot_utils.go b/cc/snapshot_utils.go index 1c872c2c9..8f48f869b 100644 --- a/cc/snapshot_utils.go +++ b/cc/snapshot_utils.go @@ -31,6 +31,33 @@ type snapshotLibraryInterface interface { var _ snapshotLibraryInterface = (*prebuiltLibraryLinker)(nil) var _ snapshotLibraryInterface = (*libraryDecorator)(nil) +type snapshotMap struct { + snapshots map[string]string +} + +func newSnapshotMap() *snapshotMap { + return &snapshotMap{ + snapshots: make(map[string]string), + } +} + +func snapshotMapKey(name string, arch android.ArchType) string { + return name + ":" + arch.String() +} + +// Adds a snapshot name for given module name and architecture. +// e.g. add("libbase", X86, "libbase.vndk.29.x86") +func (s *snapshotMap) add(name string, arch android.ArchType, snapshot string) { + s.snapshots[snapshotMapKey(name, arch)] = snapshot +} + +// Returns snapshot name for given module name and architecture, if found. +// e.g. get("libcutils", X86) => "libcutils.vndk.29.x86", true +func (s *snapshotMap) get(name string, arch android.ArchType) (snapshot string, found bool) { + snapshot, found = s.snapshots[snapshotMapKey(name, arch)] + return snapshot, found +} + func exportedHeaders(ctx android.SingletonContext, l exportedFlagsProducer) android.Paths { var ret android.Paths @@ -151,6 +151,14 @@ func needsLibAndroidSupport(ctx BaseModuleContext) bool { return version < 21 } +func staticUnwinder(ctx android.BaseModuleContext) string { + if ctx.Arch().ArchType == android.Arm { + return "libunwind_llvm" + } else { + return "libgcc_stripped" + } +} + func (stl *stl) deps(ctx BaseModuleContext, deps Deps) Deps { switch stl.Properties.SelectedStl { case "libstdc++": @@ -172,16 +180,16 @@ func (stl *stl) deps(ctx BaseModuleContext, deps Deps) Deps { } if ctx.toolchain().Bionic() { if ctx.staticBinary() { - deps.StaticLibs = append(deps.StaticLibs, "libm", "libc") - if ctx.Arch().ArchType == android.Arm { - deps.StaticLibs = append(deps.StaticLibs, "libunwind_llvm") - } else { - deps.StaticLibs = append(deps.StaticLibs, "libgcc_stripped") - } + deps.StaticLibs = append(deps.StaticLibs, "libm", "libc", staticUnwinder(ctx)) + } else { + deps.StaticUnwinderIfLegacy = true } } case "": // None or error. + if ctx.toolchain().Bionic() && ctx.Module().Name() == "libc++" { + deps.StaticUnwinderIfLegacy = true + } case "ndk_system": // TODO: Make a system STL prebuilt for the NDK. // The system STL doesn't have a prebuilt (it uses the system's libstdc++), but it does have diff --git a/cc/vendor_snapshot.go b/cc/vendor_snapshot.go index d952a4cb1..d92caa1e1 100644 --- a/cc/vendor_snapshot.go +++ b/cc/vendor_snapshot.go @@ -18,14 +18,358 @@ import ( "path/filepath" "sort" "strings" + "sync" "github.com/google/blueprint/proptools" "android/soong/android" ) +const ( + vendorSnapshotHeaderSuffix = ".vendor_header." + vendorSnapshotSharedSuffix = ".vendor_shared." + vendorSnapshotStaticSuffix = ".vendor_static." + vendorSnapshotBinarySuffix = ".vendor_binary." +) + +var ( + vendorSnapshotsLock sync.Mutex + vendorSuffixModulesKey = android.NewOnceKey("vendorSuffixModules") + vendorSnapshotHeaderLibsKey = android.NewOnceKey("vendorSnapshotHeaderLibs") + vendorSnapshotStaticLibsKey = android.NewOnceKey("vendorSnapshotStaticLibs") + vendorSnapshotSharedLibsKey = android.NewOnceKey("vendorSnapshotSharedLibs") + vendorSnapshotBinariesKey = android.NewOnceKey("vendorSnapshotBinaries") +) + +// vendor snapshot maps hold names of vendor snapshot modules per arch. +func vendorSuffixModules(config android.Config) map[string]bool { + return config.Once(vendorSuffixModulesKey, func() interface{} { + return make(map[string]bool) + }).(map[string]bool) +} + +func vendorSnapshotHeaderLibs(config android.Config) *snapshotMap { + return config.Once(vendorSnapshotHeaderLibsKey, func() interface{} { + return newSnapshotMap() + }).(*snapshotMap) +} + +func vendorSnapshotSharedLibs(config android.Config) *snapshotMap { + return config.Once(vendorSnapshotSharedLibsKey, func() interface{} { + return newSnapshotMap() + }).(*snapshotMap) +} + +func vendorSnapshotStaticLibs(config android.Config) *snapshotMap { + return config.Once(vendorSnapshotStaticLibsKey, func() interface{} { + return newSnapshotMap() + }).(*snapshotMap) +} + +func vendorSnapshotBinaries(config android.Config) *snapshotMap { + return config.Once(vendorSnapshotBinariesKey, func() interface{} { + return newSnapshotMap() + }).(*snapshotMap) +} + +type vendorSnapshotLibraryProperties struct { + // snapshot version. + Version string + + // Target arch name of the snapshot (e.g. 'arm64' for variant 'aosp_arm64') + Target_arch string + + // Prebuilt file for each arch. + Src *string `android:"arch_variant"` + + // list of flags that will be used for any module that links against this module. + Export_flags []string `android:"arch_variant"` + + // Check the prebuilt ELF files (e.g. DT_SONAME, DT_NEEDED, resolution of undefined symbols, + // etc). + Check_elf_files *bool + + // Whether this prebuilt needs to depend on sanitize ubsan runtime or not. + Sanitize_ubsan_dep *bool `android:"arch_variant"` + + // Whether this prebuilt needs to depend on sanitize minimal runtime or not. + Sanitize_minimal_dep *bool `android:"arch_variant"` +} + +type vendorSnapshotLibraryDecorator struct { + *libraryDecorator + properties vendorSnapshotLibraryProperties + androidMkVendorSuffix bool +} + +func (p *vendorSnapshotLibraryDecorator) Name(name string) string { + return name + p.NameSuffix() +} + +func (p *vendorSnapshotLibraryDecorator) NameSuffix() string { + versionSuffix := p.version() + if p.arch() != "" { + versionSuffix += "." + p.arch() + } + + var linkageSuffix string + if p.buildShared() { + linkageSuffix = vendorSnapshotSharedSuffix + } else if p.buildStatic() { + linkageSuffix = vendorSnapshotStaticSuffix + } else { + linkageSuffix = vendorSnapshotHeaderSuffix + } + + return linkageSuffix + versionSuffix +} + +func (p *vendorSnapshotLibraryDecorator) version() string { + return p.properties.Version +} + +func (p *vendorSnapshotLibraryDecorator) arch() string { + return p.properties.Target_arch +} + +func (p *vendorSnapshotLibraryDecorator) linkerFlags(ctx ModuleContext, flags Flags) Flags { + p.libraryDecorator.libName = strings.TrimSuffix(ctx.ModuleName(), p.NameSuffix()) + return p.libraryDecorator.linkerFlags(ctx, flags) +} + +func (p *vendorSnapshotLibraryDecorator) matchesWithDevice(config android.DeviceConfig) bool { + arches := config.Arches() + if len(arches) == 0 || arches[0].ArchType.String() != p.arch() { + return false + } + if !p.header() && p.properties.Src == nil { + return false + } + return true +} + +func (p *vendorSnapshotLibraryDecorator) link(ctx ModuleContext, + flags Flags, deps PathDeps, objs Objects) android.Path { + m := ctx.Module().(*Module) + p.androidMkVendorSuffix = vendorSuffixModules(ctx.Config())[m.BaseModuleName()] + + if p.header() { + return p.libraryDecorator.link(ctx, flags, deps, objs) + } + + if !p.matchesWithDevice(ctx.DeviceConfig()) { + return nil + } + + p.libraryDecorator.exportIncludes(ctx) + p.libraryDecorator.reexportFlags(p.properties.Export_flags...) + + in := android.PathForModuleSrc(ctx, *p.properties.Src) + p.unstrippedOutputFile = in + + if p.shared() { + libName := in.Base() + builderFlags := flagsToBuilderFlags(flags) + + // Optimize out relinking against shared libraries whose interface hasn't changed by + // depending on a table of contents file instead of the library itself. + tocFile := android.PathForModuleOut(ctx, libName+".toc") + p.tocFile = android.OptionalPathForPath(tocFile) + TransformSharedObjectToToc(ctx, in, tocFile, builderFlags) + } + + return in +} + +func (p *vendorSnapshotLibraryDecorator) nativeCoverage() bool { + return false +} + +func (p *vendorSnapshotLibraryDecorator) install(ctx ModuleContext, file android.Path) { + if p.matchesWithDevice(ctx.DeviceConfig()) && (p.shared() || p.static()) { + p.baseInstaller.install(ctx, file) + } +} + +type vendorSnapshotInterface interface { + version() string +} + +func vendorSnapshotLoadHook(ctx android.LoadHookContext, p vendorSnapshotInterface) { + if p.version() != ctx.DeviceConfig().VndkVersion() { + ctx.Module().Disable() + return + } +} + +func vendorSnapshotLibrary() (*Module, *vendorSnapshotLibraryDecorator) { + module, library := NewLibrary(android.DeviceSupported) + + module.stl = nil + module.sanitize = nil + library.StripProperties.Strip.None = BoolPtr(true) + + prebuilt := &vendorSnapshotLibraryDecorator{ + libraryDecorator: library, + } + + prebuilt.baseLinker.Properties.No_libcrt = BoolPtr(true) + prebuilt.baseLinker.Properties.Nocrt = BoolPtr(true) + + // Prevent default system libs (libc, libm, and libdl) from being linked + if prebuilt.baseLinker.Properties.System_shared_libs == nil { + prebuilt.baseLinker.Properties.System_shared_libs = []string{} + } + + module.compiler = nil + module.linker = prebuilt + module.installer = prebuilt + + module.AddProperties( + &prebuilt.properties, + ) + + return module, prebuilt +} + +func VendorSnapshotSharedFactory() android.Module { + module, prebuilt := vendorSnapshotLibrary() + prebuilt.libraryDecorator.BuildOnlyShared() + android.AddLoadHook(module, func(ctx android.LoadHookContext) { + vendorSnapshotLoadHook(ctx, prebuilt) + }) + return module.Init() +} + +func VendorSnapshotStaticFactory() android.Module { + module, prebuilt := vendorSnapshotLibrary() + prebuilt.libraryDecorator.BuildOnlyStatic() + android.AddLoadHook(module, func(ctx android.LoadHookContext) { + vendorSnapshotLoadHook(ctx, prebuilt) + }) + return module.Init() +} + +func VendorSnapshotHeaderFactory() android.Module { + module, prebuilt := vendorSnapshotLibrary() + prebuilt.libraryDecorator.HeaderOnly() + android.AddLoadHook(module, func(ctx android.LoadHookContext) { + vendorSnapshotLoadHook(ctx, prebuilt) + }) + return module.Init() +} + +type vendorSnapshotBinaryProperties struct { + // snapshot version. + Version string + + // Target arch name of the snapshot (e.g. 'arm64' for variant 'aosp_arm64_ab') + Target_arch string + + // Prebuilt file for each arch. + Src *string `android:"arch_variant"` +} + +type vendorSnapshotBinaryDecorator struct { + *binaryDecorator + properties vendorSnapshotBinaryProperties + androidMkVendorSuffix bool +} + +func (p *vendorSnapshotBinaryDecorator) Name(name string) string { + return name + p.NameSuffix() +} + +func (p *vendorSnapshotBinaryDecorator) NameSuffix() string { + versionSuffix := p.version() + if p.arch() != "" { + versionSuffix += "." + p.arch() + } + return vendorSnapshotBinarySuffix + versionSuffix +} + +func (p *vendorSnapshotBinaryDecorator) version() string { + return p.properties.Version +} + +func (p *vendorSnapshotBinaryDecorator) arch() string { + return p.properties.Target_arch +} + +func (p *vendorSnapshotBinaryDecorator) matchesWithDevice(config android.DeviceConfig) bool { + if config.DeviceArch() != p.arch() { + return false + } + if p.properties.Src == nil { + return false + } + return true +} + +func (p *vendorSnapshotBinaryDecorator) link(ctx ModuleContext, + flags Flags, deps PathDeps, objs Objects) android.Path { + if !p.matchesWithDevice(ctx.DeviceConfig()) { + return nil + } + + in := android.PathForModuleSrc(ctx, *p.properties.Src) + builderFlags := flagsToBuilderFlags(flags) + p.unstrippedOutputFile = in + binName := in.Base() + if p.needsStrip(ctx) { + stripped := android.PathForModuleOut(ctx, "stripped", binName) + p.stripExecutableOrSharedLib(ctx, in, stripped, builderFlags) + in = stripped + } + + m := ctx.Module().(*Module) + p.androidMkVendorSuffix = vendorSuffixModules(ctx.Config())[m.BaseModuleName()] + + // use cpExecutable to make it executable + outputFile := android.PathForModuleOut(ctx, binName) + ctx.Build(pctx, android.BuildParams{ + Rule: android.CpExecutable, + Description: "prebuilt", + Output: outputFile, + Input: in, + }) + + return outputFile +} + +func VendorSnapshotBinaryFactory() android.Module { + module, binary := NewBinary(android.DeviceSupported) + binary.baseLinker.Properties.No_libcrt = BoolPtr(true) + binary.baseLinker.Properties.Nocrt = BoolPtr(true) + + // Prevent default system libs (libc, libm, and libdl) from being linked + if binary.baseLinker.Properties.System_shared_libs == nil { + binary.baseLinker.Properties.System_shared_libs = []string{} + } + + prebuilt := &vendorSnapshotBinaryDecorator{ + binaryDecorator: binary, + } + + module.compiler = nil + module.sanitize = nil + module.stl = nil + module.linker = prebuilt + + android.AddLoadHook(module, func(ctx android.LoadHookContext) { + vendorSnapshotLoadHook(ctx, prebuilt) + }) + + module.AddProperties(&prebuilt.properties) + return module.Init() +} + func init() { android.RegisterSingletonType("vendor-snapshot", VendorSnapshotSingleton) + android.RegisterModuleType("vendor_snapshot_shared", VendorSnapshotSharedFactory) + android.RegisterModuleType("vendor_snapshot_static", VendorSnapshotStaticFactory) + android.RegisterModuleType("vendor_snapshot_header", VendorSnapshotHeaderFactory) + android.RegisterModuleType("vendor_snapshot_binary", VendorSnapshotBinaryFactory) } func VendorSnapshotSingleton() android.Singleton { @@ -367,3 +711,120 @@ func (c *vendorSnapshotSingleton) GenerateBuildActions(ctx android.SingletonCont func (c *vendorSnapshotSingleton) MakeVars(ctx android.MakeVarsContext) { ctx.Strict("SOONG_VENDOR_SNAPSHOT_ZIP", c.vendorSnapshotZipFile.String()) } + +type snapshotInterface interface { + matchesWithDevice(config android.DeviceConfig) bool +} + +var _ snapshotInterface = (*vndkPrebuiltLibraryDecorator)(nil) +var _ snapshotInterface = (*vendorSnapshotLibraryDecorator)(nil) +var _ snapshotInterface = (*vendorSnapshotBinaryDecorator)(nil) + +// gathers all snapshot modules for vendor, and disable unnecessary snapshots +// TODO(b/145966707): remove mutator and utilize android.Prebuilt to override source modules +func VendorSnapshotMutator(ctx android.BottomUpMutatorContext) { + vndkVersion := ctx.DeviceConfig().VndkVersion() + // don't need snapshot if current + if vndkVersion == "current" || vndkVersion == "" { + return + } + + module, ok := ctx.Module().(*Module) + if !ok || !module.Enabled() || module.VndkVersion() != vndkVersion { + return + } + + snapshot, ok := module.linker.(snapshotInterface) + if !ok { + return + } + + if !snapshot.matchesWithDevice(ctx.DeviceConfig()) { + // Disable unnecessary snapshot module, but do not disable + // vndk_prebuilt_shared because they might be packed into vndk APEX + if !module.IsVndk() { + module.Disable() + } + return + } + + var snapshotMap *snapshotMap + + if lib, ok := module.linker.(libraryInterface); ok { + if lib.static() { + snapshotMap = vendorSnapshotStaticLibs(ctx.Config()) + } else if lib.shared() { + snapshotMap = vendorSnapshotSharedLibs(ctx.Config()) + } else { + // header + snapshotMap = vendorSnapshotHeaderLibs(ctx.Config()) + } + } else if _, ok := module.linker.(*vendorSnapshotBinaryDecorator); ok { + snapshotMap = vendorSnapshotBinaries(ctx.Config()) + } else { + return + } + + vendorSnapshotsLock.Lock() + defer vendorSnapshotsLock.Unlock() + snapshotMap.add(module.BaseModuleName(), ctx.Arch().ArchType, ctx.ModuleName()) +} + +// Disables source modules which have snapshots +func VendorSnapshotSourceMutator(ctx android.BottomUpMutatorContext) { + vndkVersion := ctx.DeviceConfig().VndkVersion() + // don't need snapshot if current + if vndkVersion == "current" || vndkVersion == "" { + return + } + + module, ok := ctx.Module().(*Module) + if !ok { + return + } + + if module.HasVendorVariant() { + vendorSnapshotsLock.Lock() + defer vendorSnapshotsLock.Unlock() + + vendorSuffixModules(ctx.Config())[ctx.ModuleName()] = true + } + + if module.isSnapshotPrebuilt() || module.VndkVersion() != ctx.DeviceConfig().VndkVersion() { + // only non-snapshot modules with BOARD_VNDK_VERSION + return + } + + var snapshotMap *snapshotMap + + if lib, ok := module.linker.(libraryInterface); ok { + if lib.static() { + snapshotMap = vendorSnapshotStaticLibs(ctx.Config()) + } else if lib.shared() { + snapshotMap = vendorSnapshotSharedLibs(ctx.Config()) + } else { + // header + snapshotMap = vendorSnapshotHeaderLibs(ctx.Config()) + } + } else if _, ok := module.linker.(*binaryDecorator); ok { + snapshotMap = vendorSnapshotBinaries(ctx.Config()) + } else if _, ok := module.linker.(*prebuiltBinaryLinker); ok { + snapshotMap = vendorSnapshotBinaries(ctx.Config()) + } else { + return + } + + if _, ok := snapshotMap.get(ctx.ModuleName(), ctx.Arch().ArchType); !ok { + // Corresponding snapshot doesn't exist + return + } + + // Disables source modules if corresponding snapshot exists. + if lib, ok := module.linker.(libraryInterface); ok && lib.buildStatic() && lib.buildShared() { + // But do not disable because the shared variant depends on the static variant. + module.SkipInstall() + module.Properties.HideFromMake = true + } else { + module.Disable() + } +} diff --git a/cc/vndk_prebuilt.go b/cc/vndk_prebuilt.go index c941c4691..50bc32592 100644 --- a/cc/vndk_prebuilt.go +++ b/cc/vndk_prebuilt.go @@ -31,7 +31,8 @@ var ( // // vndk_prebuilt_shared { // name: "libfoo", -// version: "27.1.0", +// version: "27", +// target_arch: "arm64", // vendor_available: true, // vndk: { // enabled: true, @@ -61,10 +62,6 @@ type vndkPrebuiltProperties struct { // Prebuilt files for each arch. Srcs []string `android:"arch_variant"` - // list of directories relative to the Blueprints file that will be added to the include - // path (using -isystem) for any module that links against this module. - Export_system_include_dirs []string `android:"arch_variant"` - // list of flags that will be used for any module that links against this module. Export_flags []string `android:"arch_variant"` @@ -137,11 +134,26 @@ func (p *vndkPrebuiltLibraryDecorator) link(ctx ModuleContext, if len(p.properties.Srcs) > 0 && p.shared() { p.libraryDecorator.exportIncludes(ctx) - p.libraryDecorator.reexportSystemDirs( - android.PathsForModuleSrc(ctx, p.properties.Export_system_include_dirs)...) p.libraryDecorator.reexportFlags(p.properties.Export_flags...) // current VNDK prebuilts are only shared libs. - return p.singleSourcePath(ctx) + + in := p.singleSourcePath(ctx) + builderFlags := flagsToBuilderFlags(flags) + p.unstrippedOutputFile = in + libName := in.Base() + if p.needsStrip(ctx) { + stripped := android.PathForModuleOut(ctx, "stripped", libName) + p.stripExecutableOrSharedLib(ctx, in, stripped, builderFlags) + in = stripped + } + + // Optimize out relinking against shared libraries whose interface hasn't changed by + // depending on a table of contents file instead of the library itself. + tocFile := android.PathForModuleOut(ctx, libName+".toc") + p.tocFile = android.OptionalPathForPath(tocFile) + TransformSharedObjectToToc(ctx, in, tocFile, builderFlags) + + return in } ctx.Module().SkipInstall() @@ -212,6 +224,15 @@ func vndkPrebuiltSharedLibrary() *Module { &prebuilt.properties, ) + android.AddLoadHook(module, func(ctx android.LoadHookContext) { + // Only vndk snapshots of BOARD_VNDK_VERSION will be used when building. + if prebuilt.version() != ctx.DeviceConfig().VndkVersion() { + module.SkipInstall() + module.Properties.HideFromMake = true + return + } + }) + return module } @@ -220,7 +241,8 @@ func vndkPrebuiltSharedLibrary() *Module { // // vndk_prebuilt_shared { // name: "libfoo", -// version: "27.1.0", +// version: "27", +// target_arch: "arm64", // vendor_available: true, // vndk: { // enabled: true, diff --git a/cc/xom.go b/cc/xom.go deleted file mode 100644 index ce817aad6..000000000 --- a/cc/xom.go +++ /dev/null @@ -1,79 +0,0 @@ -// 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 cc - -import ( - "android/soong/android" -) - -type XomProperties struct { - Xom *bool -} - -type xom struct { - Properties XomProperties -} - -func (xom *xom) props() []interface{} { - return []interface{}{&xom.Properties} -} - -func (xom *xom) begin(ctx BaseModuleContext) {} - -func (xom *xom) deps(ctx BaseModuleContext, deps Deps) Deps { - return deps -} - -func (xom *xom) flags(ctx ModuleContext, flags Flags) Flags { - disableXom := false - - if !ctx.Config().EnableXOM() || ctx.Config().XOMDisabledForPath(ctx.ModuleDir()) { - disableXom = true - } - - if xom.Properties.Xom != nil && !*xom.Properties.Xom { - return flags - } - - // If any static dependencies have XOM disabled, we should disable XOM in this module, - // the assumption being if it's been explicitly disabled then there's probably incompatible - // code in the library which may get pulled in. - if !disableXom { - ctx.VisitDirectDeps(func(m android.Module) { - cc, ok := m.(*Module) - if !ok || cc.xom == nil || !cc.static() { - return - } - if cc.xom.Properties.Xom != nil && !*cc.xom.Properties.Xom { - disableXom = true - return - } - }) - } - - // Enable execute-only if none of the dependencies disable it, - // also if it's explicitly set true (allows overriding dependencies disabling it). - if !disableXom || (xom.Properties.Xom != nil && *xom.Properties.Xom) { - // XOM is only supported on AArch64 when using lld. - if ctx.Arch().ArchType == android.Arm64 && ctx.useClangLld(ctx) { - flags.Local.LdFlags = append(flags.Local.LdFlags, - "-Wl,--execute-only", - "-Wl,-z,separate-code", - ) - } - } - - return flags -} diff --git a/dexpreopt/Android.bp b/dexpreopt/Android.bp index c5f24e273..b8f7ea682 100644 --- a/dexpreopt/Android.bp +++ b/dexpreopt/Android.bp @@ -4,6 +4,7 @@ bootstrap_go_package { srcs: [ "config.go", "dexpreopt.go", + "testing.go", ], testSrcs: [ "dexpreopt_test.go", diff --git a/dexpreopt/config.go b/dexpreopt/config.go index e35387841..98850e51a 100644 --- a/dexpreopt/config.go +++ b/dexpreopt/config.go @@ -16,14 +16,16 @@ package dexpreopt import ( "encoding/json" + "fmt" "strings" + "github.com/google/blueprint" + "android/soong/android" ) // GlobalConfig stores the configuration for dex preopting. The fields are set -// from product variables via dex_preopt_config.mk, except for SoongConfig -// which come from CreateGlobalSoongConfig. +// from product variables via dex_preopt_config.mk. type GlobalConfig struct { DisablePreopt bool // disable preopt for all modules DisablePreoptModules []string // modules with preopt disabled by product-specific config @@ -81,8 +83,6 @@ type GlobalConfig struct { BootFlags string // extra flags to pass to dex2oat for the boot image Dex2oatImageXmx string // max heap size for dex2oat for the boot image Dex2oatImageXms string // initial heap size for dex2oat for the boot image - - SoongConfig GlobalSoongConfig // settings read from dexpreopt_soong.config } // GlobalSoongConfig contains the global config that is generated from Soong, @@ -178,14 +178,11 @@ func constructWritablePath(ctx android.PathContext, path string) android.Writabl return constructPath(ctx, path).(android.WritablePath) } -// LoadGlobalConfig reads the global dexpreopt.config file into a GlobalConfig -// struct, except the SoongConfig field which is set from the provided -// soongConfig argument. LoadGlobalConfig is used directly in Soong and in -// dexpreopt_gen called from Make to read the $OUT/dexpreopt.config written by -// Make. -func LoadGlobalConfig(ctx android.PathContext, data []byte, soongConfig GlobalSoongConfig) (GlobalConfig, error) { +// ParseGlobalConfig parses the given data assumed to be read from the global +// dexpreopt.config file into a GlobalConfig struct. +func ParseGlobalConfig(ctx android.PathContext, data []byte) (*GlobalConfig, error) { type GlobalJSONConfig struct { - GlobalConfig + *GlobalConfig // Copies of entries in GlobalConfig that are not constructable without extra parameters. They will be // used to construct the real value manually below. @@ -203,19 +200,70 @@ func LoadGlobalConfig(ctx android.PathContext, data []byte, soongConfig GlobalSo config.GlobalConfig.DirtyImageObjects = android.OptionalPathForPath(constructPath(ctx, config.DirtyImageObjects)) config.GlobalConfig.BootImageProfiles = constructPaths(ctx, config.BootImageProfiles) - // Set this here to force the caller to provide a value for this struct (from - // either CreateGlobalSoongConfig or LoadGlobalSoongConfig). - config.GlobalConfig.SoongConfig = soongConfig - return config.GlobalConfig, nil } -// LoadModuleConfig reads a per-module dexpreopt.config file into a ModuleConfig struct. It is not used in Soong, which -// receives a ModuleConfig struct directly from java/dexpreopt.go. It is used in dexpreopt_gen called from oMake to -// read the module dexpreopt.config written by Make. -func LoadModuleConfig(ctx android.PathContext, data []byte) (ModuleConfig, error) { +type globalConfigAndRaw struct { + global *GlobalConfig + data []byte +} + +// GetGlobalConfig returns the global dexpreopt.config that's created in the +// make config phase. It is loaded once the first time it is called for any +// ctx.Config(), and returns the same data for all future calls with the same +// ctx.Config(). A value can be inserted for tests using +// setDexpreoptTestGlobalConfig. +func GetGlobalConfig(ctx android.PathContext) *GlobalConfig { + return getGlobalConfigRaw(ctx).global +} + +// GetGlobalConfigRawData is the same as GetGlobalConfig, except that it returns +// the literal content of dexpreopt.config. +func GetGlobalConfigRawData(ctx android.PathContext) []byte { + return getGlobalConfigRaw(ctx).data +} + +var globalConfigOnceKey = android.NewOnceKey("DexpreoptGlobalConfig") +var testGlobalConfigOnceKey = android.NewOnceKey("TestDexpreoptGlobalConfig") + +func getGlobalConfigRaw(ctx android.PathContext) globalConfigAndRaw { + return ctx.Config().Once(globalConfigOnceKey, func() interface{} { + if data, err := ctx.Config().DexpreoptGlobalConfig(ctx); err != nil { + panic(err) + } else if data != nil { + globalConfig, err := ParseGlobalConfig(ctx, data) + if err != nil { + panic(err) + } + return globalConfigAndRaw{globalConfig, data} + } + + // No global config filename set, see if there is a test config set + return ctx.Config().Once(testGlobalConfigOnceKey, func() interface{} { + // Nope, return a config with preopting disabled + return globalConfigAndRaw{&GlobalConfig{ + DisablePreopt: true, + DisableGenerateProfile: true, + }, nil} + }) + }).(globalConfigAndRaw) +} + +// SetTestGlobalConfig sets a GlobalConfig that future calls to GetGlobalConfig +// will return. It must be called before the first call to GetGlobalConfig for +// the config. +func SetTestGlobalConfig(config android.Config, globalConfig *GlobalConfig) { + config.Once(testGlobalConfigOnceKey, func() interface{} { return globalConfigAndRaw{globalConfig, nil} }) +} + +// ParseModuleConfig parses a per-module dexpreopt.config file into a +// ModuleConfig struct. It is not used in Soong, which receives a ModuleConfig +// struct directly from java/dexpreopt.go. It is used in dexpreopt_gen called +// from Make to read the module dexpreopt.config written in the Make config +// stage. +func ParseModuleConfig(ctx android.PathContext, data []byte) (*ModuleConfig, error) { type ModuleJSONConfig struct { - ModuleConfig + *ModuleConfig // Copies of entries in ModuleConfig that are not constructable without extra parameters. They will be // used to construct the real value manually below. @@ -252,21 +300,63 @@ func LoadModuleConfig(ctx android.PathContext, data []byte) (ModuleConfig, error return config.ModuleConfig, nil } -// CreateGlobalSoongConfig creates a GlobalSoongConfig from the current context. -// Should not be used in dexpreopt_gen. -func CreateGlobalSoongConfig(ctx android.PathContext) GlobalSoongConfig { - // Default to debug version to help find bugs. +// dex2oatModuleName returns the name of the module to use for the dex2oat host +// tool. It should be a binary module with public visibility that is compiled +// and installed for host. +func dex2oatModuleName(config android.Config) string { + // Default to the debug variant of dex2oat to help find bugs. // Set USE_DEX2OAT_DEBUG to false for only building non-debug versions. - var dex2oatBinary string - if ctx.Config().Getenv("USE_DEX2OAT_DEBUG") == "false" { - dex2oatBinary = "dex2oat" + if config.Getenv("USE_DEX2OAT_DEBUG") == "false" { + return "dex2oat" } else { - dex2oatBinary = "dex2oatd" + return "dex2oatd" + } +} + +var dex2oatDepTag = struct { + blueprint.BaseDependencyTag +}{} + +// RegisterToolDeps adds the necessary dependencies to binary modules for tools +// that are required later when Get(Cached)GlobalSoongConfig is called. It +// should be called from a mutator that's registered with +// android.RegistrationContext.FinalDepsMutators. +func RegisterToolDeps(ctx android.BottomUpMutatorContext) { + dex2oatBin := dex2oatModuleName(ctx.Config()) + v := ctx.Config().BuildOSTarget.Variations() + ctx.AddFarVariationDependencies(v, dex2oatDepTag, dex2oatBin) +} + +func dex2oatPathFromDep(ctx android.ModuleContext) android.Path { + dex2oatBin := dex2oatModuleName(ctx.Config()) + + dex2oatModule := ctx.GetDirectDepWithTag(dex2oatBin, dex2oatDepTag) + if dex2oatModule == nil { + // If this happens there's probably a missing call to AddToolDeps in DepsMutator. + panic(fmt.Sprintf("Failed to lookup %s dependency", dex2oatBin)) + } + + dex2oatPath := dex2oatModule.(android.HostToolProvider).HostToolPath() + if !dex2oatPath.Valid() { + panic(fmt.Sprintf("Failed to find host tool path in %s", dex2oatModule)) + } + + return dex2oatPath.Path() +} + +// createGlobalSoongConfig creates a GlobalSoongConfig from the current context. +// Should not be used in dexpreopt_gen. +func createGlobalSoongConfig(ctx android.ModuleContext) *GlobalSoongConfig { + if ctx.Config().TestProductVariables != nil { + // If we're called in a test there'll be a confusing error from the path + // functions below that gets reported without a stack trace, so let's panic + // properly with a more helpful message. + panic("This should not be called from tests. Please call GlobalSoongConfigForTests somewhere in the test setup.") } - return GlobalSoongConfig{ + return &GlobalSoongConfig{ Profman: ctx.Config().HostToolPath(ctx, "profman"), - Dex2oat: ctx.Config().HostToolPath(ctx, dex2oatBinary), + Dex2oat: dex2oatPathFromDep(ctx), Aapt: ctx.Config().HostToolPath(ctx, "aapt"), SoongZip: ctx.Config().HostToolPath(ctx, "soong_zip"), Zip2zip: ctx.Config().HostToolPath(ctx, "zip2zip"), @@ -275,6 +365,47 @@ func CreateGlobalSoongConfig(ctx android.PathContext) GlobalSoongConfig { } } +// The main reason for this Once cache for GlobalSoongConfig is to make the +// dex2oat path available to singletons. In ordinary modules we get it through a +// dex2oatDepTag dependency, but in singletons there's no simple way to do the +// same thing and ensure the right variant is selected, hence this cache to make +// the resolved path available to singletons. This means we depend on there +// being at least one ordinary module with a dex2oatDepTag dependency. +// +// TODO(b/147613152): Implement a way to deal with dependencies from singletons, +// and then possibly remove this cache altogether (but the use in +// GlobalSoongConfigForTests also needs to be rethought). +var globalSoongConfigOnceKey = android.NewOnceKey("DexpreoptGlobalSoongConfig") + +// GetGlobalSoongConfig creates a GlobalSoongConfig the first time it's called, +// and later returns the same cached instance. +func GetGlobalSoongConfig(ctx android.ModuleContext) *GlobalSoongConfig { + globalSoong := ctx.Config().Once(globalSoongConfigOnceKey, func() interface{} { + return createGlobalSoongConfig(ctx) + }).(*GlobalSoongConfig) + + // Always resolve the tool path from the dependency, to ensure that every + // module has the dependency added properly. + myDex2oat := dex2oatPathFromDep(ctx) + if myDex2oat != globalSoong.Dex2oat { + panic(fmt.Sprintf("Inconsistent dex2oat path in cached config: expected %s, got %s", globalSoong.Dex2oat, myDex2oat)) + } + + return globalSoong +} + +// GetCachedGlobalSoongConfig returns a cached GlobalSoongConfig created by an +// earlier GetGlobalSoongConfig call. This function works with any context +// compatible with a basic PathContext, since it doesn't try to create a +// GlobalSoongConfig with the proper paths (which requires a full +// ModuleContext). If there has been no prior call to GetGlobalSoongConfig, nil +// is returned. +func GetCachedGlobalSoongConfig(ctx android.PathContext) *GlobalSoongConfig { + return ctx.Config().Once(globalSoongConfigOnceKey, func() interface{} { + return (*GlobalSoongConfig)(nil) + }).(*GlobalSoongConfig) +} + type globalJsonSoongConfig struct { Profman string Dex2oat string @@ -285,17 +416,18 @@ type globalJsonSoongConfig struct { ConstructContext string } -// LoadGlobalSoongConfig reads the dexpreopt_soong.config file into a -// GlobalSoongConfig struct. It is only used in dexpreopt_gen. -func LoadGlobalSoongConfig(ctx android.PathContext, data []byte) (GlobalSoongConfig, error) { +// ParseGlobalSoongConfig parses the given data assumed to be read from the +// global dexpreopt_soong.config file into a GlobalSoongConfig struct. It is +// only used in dexpreopt_gen. +func ParseGlobalSoongConfig(ctx android.PathContext, data []byte) (*GlobalSoongConfig, error) { var jc globalJsonSoongConfig err := json.Unmarshal(data, &jc) if err != nil { - return GlobalSoongConfig{}, err + return &GlobalSoongConfig{}, err } - config := GlobalSoongConfig{ + config := &GlobalSoongConfig{ Profman: constructPath(ctx, jc.Profman), Dex2oat: constructPath(ctx, jc.Dex2oat), Aapt: constructPath(ctx, jc.Aapt), @@ -309,7 +441,17 @@ func LoadGlobalSoongConfig(ctx android.PathContext, data []byte) (GlobalSoongCon } func (s *globalSoongConfigSingleton) GenerateBuildActions(ctx android.SingletonContext) { - config := CreateGlobalSoongConfig(ctx) + if GetGlobalConfig(ctx).DisablePreopt { + return + } + + config := GetCachedGlobalSoongConfig(ctx) + if config == nil { + // No module has enabled dexpreopting, so we assume there will be no calls + // to dexpreopt_gen. + return + } + jc := globalJsonSoongConfig{ Profman: config.Profman.String(), Dex2oat: config.Dex2oat.String(), @@ -336,7 +478,14 @@ func (s *globalSoongConfigSingleton) GenerateBuildActions(ctx android.SingletonC } func (s *globalSoongConfigSingleton) MakeVars(ctx android.MakeVarsContext) { - config := CreateGlobalSoongConfig(ctx) + if GetGlobalConfig(ctx).DisablePreopt { + return + } + + config := GetCachedGlobalSoongConfig(ctx) + if config == nil { + return + } ctx.Strict("DEX2OAT", config.Dex2oat.String()) ctx.Strict("DEXPREOPT_GEN_DEPS", strings.Join([]string{ @@ -350,8 +499,8 @@ func (s *globalSoongConfigSingleton) MakeVars(ctx android.MakeVarsContext) { }, " ")) } -func GlobalConfigForTests(ctx android.PathContext) GlobalConfig { - return GlobalConfig{ +func GlobalConfigForTests(ctx android.PathContext) *GlobalConfig { + return &GlobalConfig{ DisablePreopt: false, DisablePreoptModules: nil, OnlyPreoptBootImageAndSystemServer: false, @@ -389,7 +538,14 @@ func GlobalConfigForTests(ctx android.PathContext) GlobalConfig { BootFlags: "", Dex2oatImageXmx: "", Dex2oatImageXms: "", - SoongConfig: GlobalSoongConfig{ + } +} + +func GlobalSoongConfigForTests(config android.Config) *GlobalSoongConfig { + // Install the test GlobalSoongConfig in the Once cache so that later calls to + // Get(Cached)GlobalSoongConfig returns it without trying to create a real one. + return config.Once(globalSoongConfigOnceKey, func() interface{} { + return &GlobalSoongConfig{ Profman: android.PathForTesting("profman"), Dex2oat: android.PathForTesting("dex2oat"), Aapt: android.PathForTesting("aapt"), @@ -397,6 +553,6 @@ func GlobalConfigForTests(ctx android.PathContext) GlobalConfig { Zip2zip: android.PathForTesting("zip2zip"), ManifestCheck: android.PathForTesting("manifest_check"), ConstructContext: android.PathForTesting("construct_context.sh"), - }, - } + } + }).(*GlobalSoongConfig) } diff --git a/dexpreopt/dexpreopt.go b/dexpreopt/dexpreopt.go index ac5b691c1..6cb987385 100644 --- a/dexpreopt/dexpreopt.go +++ b/dexpreopt/dexpreopt.go @@ -41,16 +41,25 @@ import ( "android/soong/android" + "github.com/google/blueprint" "github.com/google/blueprint/pathtools" ) const SystemPartition = "/system/" const SystemOtherPartition = "/system_other/" +type dependencyTag struct { + blueprint.BaseDependencyTag + name string +} + +var SystemServerDepTag = dependencyTag{name: "system-server-dep"} +var SystemServerForcedDepTag = dependencyTag{name: "system-server-forced-dep"} + // GenerateDexpreoptRule generates a set of commands that will preopt a module based on a GlobalConfig and a // ModuleConfig. The produced files and their install locations will be available through rule.Installs(). -func GenerateDexpreoptRule(ctx android.PathContext, - global GlobalConfig, module ModuleConfig) (rule *android.RuleBuilder, err error) { +func GenerateDexpreoptRule(ctx android.PathContext, globalSoong *GlobalSoongConfig, + global *GlobalConfig, module *ModuleConfig) (rule *android.RuleBuilder, err error) { defer func() { if r := recover(); r != nil { @@ -72,13 +81,13 @@ func GenerateDexpreoptRule(ctx android.PathContext, var profile android.WritablePath if generateProfile { - profile = profileCommand(ctx, global, module, rule) + profile = profileCommand(ctx, globalSoong, global, module, rule) } if generateBootProfile { - bootProfileCommand(ctx, global, module, rule) + bootProfileCommand(ctx, globalSoong, global, module, rule) } - if !dexpreoptDisabled(global, module) { + if !dexpreoptDisabled(ctx, global, module) { // Don't preopt individual boot jars, they will be preopted together. if !contains(global.BootJars, module.Name) { appImage := (generateProfile || module.ForceCreateAppImage || global.DefaultAppImages) && @@ -87,7 +96,7 @@ func GenerateDexpreoptRule(ctx android.PathContext, generateDM := shouldGenerateDM(module, global) for archIdx, _ := range module.Archs { - dexpreoptCommand(ctx, global, module, rule, archIdx, profile, appImage, generateDM) + dexpreoptCommand(ctx, globalSoong, global, module, rule, archIdx, profile, appImage, generateDM) } } } @@ -95,14 +104,21 @@ func GenerateDexpreoptRule(ctx android.PathContext, return rule, nil } -func dexpreoptDisabled(global GlobalConfig, module ModuleConfig) bool { +func dexpreoptDisabled(ctx android.PathContext, global *GlobalConfig, module *ModuleConfig) bool { if contains(global.DisablePreoptModules, module.Name) { return true } // Don't preopt system server jars that are updatable. for _, p := range global.UpdatableSystemServerJars { - if _, jar := SplitApexJarPair(p); jar == module.Name { + if _, jar := android.SplitApexJarPair(p); jar == module.Name { + return true + } + } + + // Don't preopt system server jars that are not Soong modules. + if android.InList(module.Name, NonUpdatableSystemServerJars(ctx, global)) { + if _, ok := ctx.(android.ModuleContext); !ok { return true } } @@ -119,8 +135,8 @@ func dexpreoptDisabled(global GlobalConfig, module ModuleConfig) bool { return false } -func profileCommand(ctx android.PathContext, global GlobalConfig, module ModuleConfig, - rule *android.RuleBuilder) android.WritablePath { +func profileCommand(ctx android.PathContext, globalSoong *GlobalSoongConfig, global *GlobalConfig, + module *ModuleConfig, rule *android.RuleBuilder) android.WritablePath { profilePath := module.BuildPath.InSameDir(ctx, "profile.prof") profileInstalledPath := module.DexLocation + ".prof" @@ -131,7 +147,7 @@ func profileCommand(ctx android.PathContext, global GlobalConfig, module ModuleC cmd := rule.Command(). Text(`ANDROID_LOG_TAGS="*:e"`). - Tool(global.SoongConfig.Profman) + Tool(globalSoong.Profman) if module.ProfileIsTextListing { // The profile is a test listing of classes (used for framework jars). @@ -158,8 +174,8 @@ func profileCommand(ctx android.PathContext, global GlobalConfig, module ModuleC return profilePath } -func bootProfileCommand(ctx android.PathContext, global GlobalConfig, module ModuleConfig, - rule *android.RuleBuilder) android.WritablePath { +func bootProfileCommand(ctx android.PathContext, globalSoong *GlobalSoongConfig, global *GlobalConfig, + module *ModuleConfig, rule *android.RuleBuilder) android.WritablePath { profilePath := module.BuildPath.InSameDir(ctx, "profile.bprof") profileInstalledPath := module.DexLocation + ".bprof" @@ -170,7 +186,7 @@ func bootProfileCommand(ctx android.PathContext, global GlobalConfig, module Mod cmd := rule.Command(). Text(`ANDROID_LOG_TAGS="*:e"`). - Tool(global.SoongConfig.Profman) + Tool(globalSoong.Profman) // The profile is a test listing of methods. // We need to generate the actual binary profile. @@ -190,8 +206,9 @@ func bootProfileCommand(ctx android.PathContext, global GlobalConfig, module Mod return profilePath } -func dexpreoptCommand(ctx android.PathContext, global GlobalConfig, module ModuleConfig, rule *android.RuleBuilder, - archIdx int, profile android.WritablePath, appImage bool, generateDM bool) { +func dexpreoptCommand(ctx android.PathContext, globalSoong *GlobalSoongConfig, global *GlobalConfig, + module *ModuleConfig, rule *android.RuleBuilder, archIdx int, profile android.WritablePath, + appImage bool, generateDM bool) { arch := module.Archs[archIdx] @@ -236,7 +253,8 @@ func dexpreoptCommand(ctx android.PathContext, global GlobalConfig, module Modul var conditionalClassLoaderContextHost29 android.Paths var conditionalClassLoaderContextTarget29 []string - var classLoaderContextHostString string + var classLoaderContextHostString, classLoaderContextDeviceString string + var classLoaderDeps android.Paths if module.EnforceUsesLibraries { usesLibs := append(copyOf(module.UsesLibraries), module.PresentOptionalUsesLibraries...) @@ -282,6 +300,30 @@ func dexpreoptCommand(ctx android.PathContext, global GlobalConfig, module Modul filepath.Join("/system/framework", hidlBase+".jar")) classLoaderContextHostString = strings.Join(classLoaderContextHost.Strings(), ":") + } else if android.InList(module.Name, NonUpdatableSystemServerJars(ctx, global)) { + // We expect that all dexpreopted system server jars are Soong modules. + mctx, isModule := ctx.(android.ModuleContext) + if !isModule { + panic("Cannot dexpreopt system server jar that is not a soong module.") + } + + // System server jars should be dexpreopted together: class loader context of each jar + // should include preceding jars (which can be found as dependencies of the current jar + // with a special tag). + var jarsOnHost android.Paths + var jarsOnDevice []string + mctx.VisitDirectDepsWithTag(SystemServerDepTag, func(dep android.Module) { + depName := mctx.OtherModuleName(dep) + if jar, ok := dep.(interface{ DexJar() android.Path }); ok { + jarsOnHost = append(jarsOnHost, jar.DexJar()) + jarsOnDevice = append(jarsOnDevice, "/system/framework/"+depName+".jar") + } else { + mctx.ModuleErrorf("module \"%s\" is not a jar", depName) + } + }) + classLoaderContextHostString = strings.Join(jarsOnHost.Strings(), ":") + classLoaderContextDeviceString = strings.Join(jarsOnDevice, ":") + classLoaderDeps = jarsOnHost } else { // Pass special class loader context to skip the classpath and collision check. // This will get removed once LOCAL_USES_LIBRARIES is enforced. @@ -293,20 +335,25 @@ func dexpreoptCommand(ctx android.PathContext, global GlobalConfig, module Modul rule.Command().FlagWithArg("mkdir -p ", filepath.Dir(odexPath.String())) rule.Command().FlagWithOutput("rm -f ", odexPath) // Set values in the environment of the rule. These may be modified by construct_context.sh. - rule.Command().FlagWithArg("class_loader_context_arg=--class-loader-context=", classLoaderContextHostString) - rule.Command().Text(`stored_class_loader_context_arg=""`) + if classLoaderContextHostString == `\&` { + rule.Command().Text(`class_loader_context_arg=--class-loader-context=\&`) + rule.Command().Text(`stored_class_loader_context_arg=""`) + } else { + rule.Command().Text("class_loader_context_arg=--class-loader-context=PCL[" + classLoaderContextHostString + "]") + rule.Command().Text("stored_class_loader_context_arg=--stored-class-loader-context=PCL[" + classLoaderContextDeviceString + "]") + } if module.EnforceUsesLibraries { if module.ManifestPath != nil { rule.Command().Text(`target_sdk_version="$(`). - Tool(global.SoongConfig.ManifestCheck). + Tool(globalSoong.ManifestCheck). Flag("--extract-target-sdk-version"). Input(module.ManifestPath). Text(`)"`) } else { // No manifest to extract targetSdkVersion from, hope that DexJar is an APK rule.Command().Text(`target_sdk_version="$(`). - Tool(global.SoongConfig.Aapt). + Tool(globalSoong.Aapt). Flag("dump badging"). Input(module.DexPath). Text(`| grep "targetSdkVersion" | sed -n "s/targetSdkVersion:'\(.*\)'/\1/p"`). @@ -327,7 +374,7 @@ func dexpreoptCommand(ctx android.PathContext, global GlobalConfig, module Modul Implicits(conditionalClassLoaderContextHost29) rule.Command().Textf(`conditional_target_libs_29="%s"`, strings.Join(conditionalClassLoaderContextTarget29, " ")) - rule.Command().Text("source").Tool(global.SoongConfig.ConstructContext).Input(module.DexPath) + rule.Command().Text("source").Tool(globalSoong.ConstructContext).Input(module.DexPath) } // Devices that do not have a product partition use a symlink from /product to /system/product. @@ -340,7 +387,7 @@ func dexpreoptCommand(ctx android.PathContext, global GlobalConfig, module Modul cmd := rule.Command(). Text(`ANDROID_LOG_TAGS="*:e"`). - Tool(global.SoongConfig.Dex2oat). + Tool(globalSoong.Dex2oat). Flag("--avoid-storing-invocation"). FlagWithOutput("--write-invocation-to=", invocationPath).ImplicitOutput(invocationPath). Flag("--runtime-arg").FlagWithArg("-Xms", global.Dex2oatXms). @@ -348,7 +395,7 @@ func dexpreoptCommand(ctx android.PathContext, global GlobalConfig, module Modul Flag("--runtime-arg").FlagWithInputList("-Xbootclasspath:", module.PreoptBootClassPathDexFiles, ":"). Flag("--runtime-arg").FlagWithList("-Xbootclasspath-locations:", module.PreoptBootClassPathDexLocations, ":"). Flag("${class_loader_context_arg}"). - Flag("${stored_class_loader_context_arg}"). + Flag("${stored_class_loader_context_arg}").Implicits(classLoaderDeps). FlagWithArg("--boot-image=", strings.Join(module.DexPreoptImageLocations, ":")).Implicits(module.DexPreoptImagesDeps[archIdx].Paths()). FlagWithInput("--dex-file=", module.DexPath). FlagWithArg("--dex-location=", dexLocationArg). @@ -379,7 +426,7 @@ func dexpreoptCommand(ctx android.PathContext, global GlobalConfig, module Modul cmd.FlagWithArg("--copy-dex-files=", "false") } - if !anyHavePrefix(preoptFlags, "--compiler-filter=") { + if !android.PrefixInList(preoptFlags, "--compiler-filter=") { var compilerFilter string if contains(global.SystemServerJars, module.Name) { // Jars of system server, use the product option if it is set, speed otherwise. @@ -409,7 +456,7 @@ func dexpreoptCommand(ctx android.PathContext, global GlobalConfig, module Modul dmInstalledPath := pathtools.ReplaceExtension(module.DexLocation, "dm") tmpPath := module.BuildPath.InSameDir(ctx, "primary.vdex") rule.Command().Text("cp -f").Input(vdexPath).Output(tmpPath) - rule.Command().Tool(global.SoongConfig.SoongZip). + rule.Command().Tool(globalSoong.SoongZip). FlagWithArg("-L", "9"). FlagWithOutput("-o", dmPath). Flag("-j"). @@ -474,14 +521,14 @@ func dexpreoptCommand(ctx android.PathContext, global GlobalConfig, module Modul rule.Install(vdexPath, vdexInstallPath) } -func shouldGenerateDM(module ModuleConfig, global GlobalConfig) bool { +func shouldGenerateDM(module *ModuleConfig, global *GlobalConfig) bool { // Generating DM files only makes sense for verify, avoid doing for non verify compiler filter APKs. // No reason to use a dm file if the dex is already uncompressed. return global.GenerateDMFiles && !module.UncompressedDex && contains(module.PreoptFlags, "--compiler-filter=verify") } -func OdexOnSystemOtherByName(name string, dexLocation string, global GlobalConfig) bool { +func OdexOnSystemOtherByName(name string, dexLocation string, global *GlobalConfig) bool { if !global.HasSystemOther { return false } @@ -503,7 +550,7 @@ func OdexOnSystemOtherByName(name string, dexLocation string, global GlobalConfi return false } -func odexOnSystemOther(module ModuleConfig, global GlobalConfig) bool { +func odexOnSystemOther(module *ModuleConfig, global *GlobalConfig) bool { return OdexOnSystemOtherByName(module.Name, module.DexLocation, global) } @@ -516,7 +563,7 @@ func PathToLocation(path android.Path, arch android.ArchType) string { return filepath.Join(filepath.Dir(filepath.Dir(path.String())), filepath.Base(path.String())) } -func pathForLibrary(module ModuleConfig, lib string) android.Path { +func pathForLibrary(module *ModuleConfig, lib string) android.Path { path, ok := module.LibraryPaths[lib] if !ok { panic(fmt.Errorf("unknown library path for %q", lib)) @@ -537,56 +584,38 @@ func makefileMatch(pattern, s string) bool { } // Expected format for apexJarValue = <apex name>:<jar name> -func SplitApexJarPair(apexJarValue string) (string, string) { - var apexJarPair []string = strings.SplitN(apexJarValue, ":", 2) - if apexJarPair == nil || len(apexJarPair) != 2 { - panic(fmt.Errorf("malformed apexJarValue: %q, expected format: <apex>:<jar>", - apexJarValue)) - } - return apexJarPair[0], apexJarPair[1] -} - -// Expected format for apexJarValue = <apex name>:<jar name> func GetJarLocationFromApexJarPair(apexJarValue string) string { - apex, jar := SplitApexJarPair(apexJarValue) + apex, jar := android.SplitApexJarPair(apexJarValue) return filepath.Join("/apex", apex, "javalib", jar+".jar") } -func contains(l []string, s string) bool { - for _, e := range l { - if e == s { - return true - } +func GetJarsFromApexJarPairs(apexJarPairs []string) []string { + modules := make([]string, len(apexJarPairs)) + for i, p := range apexJarPairs { + _, jar := android.SplitApexJarPair(p) + modules[i] = jar } - return false + return modules } -// remove all elements in a from b, returning a new slice -func filterOut(a []string, b []string) []string { - var ret []string - for _, x := range b { - if !contains(a, x) { - ret = append(ret, x) - } - } - return ret -} +var nonUpdatableSystemServerJarsKey = android.NewOnceKey("nonUpdatableSystemServerJars") -func replace(l []string, from, to string) { - for i := range l { - if l[i] == from { - l[i] = to - } - } +// TODO: eliminate the superficial global config parameter by moving global config definition +// from java subpackage to dexpreopt. +func NonUpdatableSystemServerJars(ctx android.PathContext, global *GlobalConfig) []string { + return ctx.Config().Once(nonUpdatableSystemServerJarsKey, func() interface{} { + return android.RemoveListFromList(global.SystemServerJars, + GetJarsFromApexJarPairs(global.UpdatableSystemServerJars)) + }).([]string) } -var copyOf = android.CopyOf - -func anyHavePrefix(l []string, prefix string) bool { - for _, x := range l { - if strings.HasPrefix(x, prefix) { +func contains(l []string, s string) bool { + for _, e := range l { + if e == s { return true } } return false } + +var copyOf = android.CopyOf diff --git a/dexpreopt/dexpreopt_gen/dexpreopt_gen.go b/dexpreopt/dexpreopt_gen/dexpreopt_gen.go index e2818bb61..e89f04591 100644 --- a/dexpreopt/dexpreopt_gen/dexpreopt_gen.go +++ b/dexpreopt/dexpreopt_gen/dexpreopt_gen.go @@ -80,13 +80,13 @@ func main() { globalSoongConfigData, err := ioutil.ReadFile(*globalSoongConfigPath) if err != nil { - fmt.Fprintf(os.Stderr, "error reading global config %q: %s\n", *globalSoongConfigPath, err) + fmt.Fprintf(os.Stderr, "error reading global Soong config %q: %s\n", *globalSoongConfigPath, err) os.Exit(2) } - globalSoongConfig, err := dexpreopt.LoadGlobalSoongConfig(ctx, globalSoongConfigData) + globalSoongConfig, err := dexpreopt.ParseGlobalSoongConfig(ctx, globalSoongConfigData) if err != nil { - fmt.Fprintf(os.Stderr, "error loading global config %q: %s\n", *globalSoongConfigPath, err) + fmt.Fprintf(os.Stderr, "error parsing global Soong config %q: %s\n", *globalSoongConfigPath, err) os.Exit(2) } @@ -96,9 +96,9 @@ func main() { os.Exit(2) } - globalConfig, err := dexpreopt.LoadGlobalConfig(ctx, globalConfigData, globalSoongConfig) + globalConfig, err := dexpreopt.ParseGlobalConfig(ctx, globalConfigData) if err != nil { - fmt.Fprintf(os.Stderr, "error parse global config %q: %s\n", *globalConfigPath, err) + fmt.Fprintf(os.Stderr, "error parsing global config %q: %s\n", *globalConfigPath, err) os.Exit(2) } @@ -108,9 +108,9 @@ func main() { os.Exit(2) } - moduleConfig, err := dexpreopt.LoadModuleConfig(ctx, moduleConfigData) + moduleConfig, err := dexpreopt.ParseModuleConfig(ctx, moduleConfigData) if err != nil { - fmt.Fprintf(os.Stderr, "error loading module config %q: %s\n", *moduleConfigPath, err) + fmt.Fprintf(os.Stderr, "error parsing module config %q: %s\n", *moduleConfigPath, err) os.Exit(2) } @@ -130,12 +130,12 @@ func main() { } }() - writeScripts(ctx, globalConfig, moduleConfig, *dexpreoptScriptPath) + writeScripts(ctx, globalSoongConfig, globalConfig, moduleConfig, *dexpreoptScriptPath) } -func writeScripts(ctx android.PathContext, global dexpreopt.GlobalConfig, module dexpreopt.ModuleConfig, - dexpreoptScriptPath string) { - dexpreoptRule, err := dexpreopt.GenerateDexpreoptRule(ctx, global, module) +func writeScripts(ctx android.PathContext, globalSoong *dexpreopt.GlobalSoongConfig, + global *dexpreopt.GlobalConfig, module *dexpreopt.ModuleConfig, dexpreoptScriptPath string) { + dexpreoptRule, err := dexpreopt.GenerateDexpreoptRule(ctx, globalSoong, global, module) if err != nil { panic(err) } @@ -150,7 +150,7 @@ func writeScripts(ctx android.PathContext, global dexpreopt.GlobalConfig, module dexpreoptRule.Command().Text("mkdir -p").Flag(filepath.Dir(installPath.String())) dexpreoptRule.Command().Text("cp -f").Input(install.From).Output(installPath) } - dexpreoptRule.Command().Tool(global.SoongConfig.SoongZip). + dexpreoptRule.Command().Tool(globalSoong.SoongZip). FlagWithArg("-o ", "$2"). FlagWithArg("-C ", installDir.String()). FlagWithArg("-D ", installDir.String()) diff --git a/dexpreopt/dexpreopt_test.go b/dexpreopt/dexpreopt_test.go index a128dc009..d23999328 100644 --- a/dexpreopt/dexpreopt_test.go +++ b/dexpreopt/dexpreopt_test.go @@ -20,20 +20,20 @@ import ( "testing" ) -func testSystemModuleConfig(ctx android.PathContext, name string) ModuleConfig { +func testSystemModuleConfig(ctx android.PathContext, name string) *ModuleConfig { return testModuleConfig(ctx, name, "system") } -func testSystemProductModuleConfig(ctx android.PathContext, name string) ModuleConfig { +func testSystemProductModuleConfig(ctx android.PathContext, name string) *ModuleConfig { return testModuleConfig(ctx, name, "system/product") } -func testProductModuleConfig(ctx android.PathContext, name string) ModuleConfig { +func testProductModuleConfig(ctx android.PathContext, name string) *ModuleConfig { return testModuleConfig(ctx, name, "product") } -func testModuleConfig(ctx android.PathContext, name, partition string) ModuleConfig { - return ModuleConfig{ +func testModuleConfig(ctx android.PathContext, name, partition string) *ModuleConfig { + return &ModuleConfig{ Name: name, DexLocation: fmt.Sprintf("/%s/app/test/%s.apk", partition, name), BuildPath: android.PathForOutput(ctx, fmt.Sprintf("%s/%s.apk", name, name)), @@ -61,10 +61,13 @@ func testModuleConfig(ctx android.PathContext, name, partition string) ModuleCon } func TestDexPreopt(t *testing.T) { - ctx := android.PathContextForTesting(android.TestConfig("out", nil, "", nil)) - global, module := GlobalConfigForTests(ctx), testSystemModuleConfig(ctx, "test") + config := android.TestConfig("out", nil, "", nil) + ctx := android.PathContextForTesting(config) + globalSoong := GlobalSoongConfigForTests(config) + global := GlobalConfigForTests(ctx) + module := testSystemModuleConfig(ctx, "test") - rule, err := GenerateDexpreoptRule(ctx, global, module) + rule, err := GenerateDexpreoptRule(ctx, globalSoong, global, module) if err != nil { t.Fatal(err) } @@ -80,7 +83,9 @@ func TestDexPreopt(t *testing.T) { } func TestDexPreoptSystemOther(t *testing.T) { - ctx := android.PathContextForTesting(android.TestConfig("out", nil, "", nil)) + config := android.TestConfig("out", nil, "", nil) + ctx := android.PathContextForTesting(config) + globalSoong := GlobalSoongConfigForTests(config) global := GlobalConfigForTests(ctx) systemModule := testSystemModuleConfig(ctx, "Stest") systemProductModule := testSystemProductModuleConfig(ctx, "SPtest") @@ -89,7 +94,7 @@ func TestDexPreoptSystemOther(t *testing.T) { global.HasSystemOther = true type moduleTest struct { - module ModuleConfig + module *ModuleConfig expectedPartition string } tests := []struct { @@ -118,7 +123,7 @@ func TestDexPreoptSystemOther(t *testing.T) { for _, test := range tests { global.PatternsOnSystemOther = test.patterns for _, mt := range test.moduleTests { - rule, err := GenerateDexpreoptRule(ctx, global, mt.module) + rule, err := GenerateDexpreoptRule(ctx, globalSoong, global, mt.module) if err != nil { t.Fatal(err) } @@ -138,12 +143,15 @@ func TestDexPreoptSystemOther(t *testing.T) { } func TestDexPreoptProfile(t *testing.T) { - ctx := android.PathContextForTesting(android.TestConfig("out", nil, "", nil)) - global, module := GlobalConfigForTests(ctx), testSystemModuleConfig(ctx, "test") + config := android.TestConfig("out", nil, "", nil) + ctx := android.PathContextForTesting(config) + globalSoong := GlobalSoongConfigForTests(config) + global := GlobalConfigForTests(ctx) + module := testSystemModuleConfig(ctx, "test") module.ProfileClassListing = android.OptionalPathForPath(android.PathForTesting("profile")) - rule, err := GenerateDexpreoptRule(ctx, global, module) + rule, err := GenerateDexpreoptRule(ctx, globalSoong, global, module) if err != nil { t.Fatal(err) } diff --git a/dexpreopt/testing.go b/dexpreopt/testing.go new file mode 100644 index 000000000..b572eb351 --- /dev/null +++ b/dexpreopt/testing.go @@ -0,0 +1,47 @@ +// Copyright 2020 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 dexpreopt + +import ( + "android/soong/android" +) + +type dummyToolBinary struct { + android.ModuleBase +} + +func (m *dummyToolBinary) GenerateAndroidBuildActions(ctx android.ModuleContext) {} + +func (m *dummyToolBinary) HostToolPath() android.OptionalPath { + return android.OptionalPathForPath(android.PathForTesting("dex2oat")) +} + +func dummyToolBinaryFactory() android.Module { + module := &dummyToolBinary{} + android.InitAndroidArchModule(module, android.HostSupported, android.MultilibFirst) + return module +} + +func RegisterToolModulesForTest(ctx *android.TestContext) { + ctx.RegisterModuleType("dummy_tool_binary", dummyToolBinaryFactory) +} + +func BpToolModulesForTest() string { + return ` + dummy_tool_binary { + name: "dex2oatd", + } + ` +} diff --git a/java/aar.go b/java/aar.go index 24c5e7d04..6e3b9e6d3 100644 --- a/java/aar.go +++ b/java/aar.go @@ -101,6 +101,7 @@ type aapt struct { usesNonSdkApis bool sdkLibraries []string hasNoCode bool + LoggingParent string splitNames []string splits []split @@ -134,15 +135,8 @@ func (a *aapt) aapt2Flags(ctx android.ModuleContext, sdkContext sdkContext, manifestPath android.Path) (compileFlags, linkFlags []string, linkDeps android.Paths, resDirs, overlayDirs []globbedResourceDir, rroDirs []rroDir, resZips android.Paths) { - hasVersionCode := false - hasVersionName := false - for _, f := range a.aaptProperties.Aaptflags { - if strings.HasPrefix(f, "--version-code") { - hasVersionCode = true - } else if strings.HasPrefix(f, "--version-name") { - hasVersionName = true - } - } + hasVersionCode := android.PrefixInList(a.aaptProperties.Aaptflags, "--version-code") + hasVersionName := android.PrefixInList(a.aaptProperties.Aaptflags, "--version-name") // Flags specified in Android.bp linkFlags = append(linkFlags, a.aaptProperties.Aaptflags...) @@ -241,7 +235,8 @@ func (a *aapt) buildActions(ctx android.ModuleContext, sdkContext sdkContext, ex manifestSrcPath := android.PathForModuleSrc(ctx, manifestFile) manifestPath := manifestFixer(ctx, manifestSrcPath, sdkContext, sdkLibraries, - a.isLibrary, a.useEmbeddedNativeLibs, a.usesNonSdkApis, a.useEmbeddedDex, a.hasNoCode) + a.isLibrary, a.useEmbeddedNativeLibs, a.usesNonSdkApis, a.useEmbeddedDex, a.hasNoCode, + a.LoggingParent) // Add additional manifest files to transitive manifests. additionalManifests := android.PathsForModuleSrc(ctx, a.aaptProperties.Additional_manifests) @@ -337,7 +332,7 @@ func (a *aapt) buildActions(ctx android.ModuleContext, sdkContext sdkContext, ex // Extract assets from the resource package output so that they can be used later in aapt2link // for modules that depend on this one. - if android.PrefixedStringInList(linkFlags, "-A ") || len(assetPackages) > 0 { + if android.PrefixInList(linkFlags, "-A ") || len(assetPackages) > 0 { assets := android.PathForModuleOut(ctx, "assets.zip") ctx.Build(pctx, android.BuildParams{ Rule: extractAssetsRule, diff --git a/java/android_manifest.go b/java/android_manifest.go index e3646f5f9..9a71be28a 100644 --- a/java/android_manifest.go +++ b/java/android_manifest.go @@ -53,7 +53,7 @@ var optionalUsesLibs = []string{ // Uses manifest_fixer.py to inject minSdkVersion, etc. into an AndroidManifest.xml func manifestFixer(ctx android.ModuleContext, manifest android.Path, sdkContext sdkContext, sdkLibraries []string, - isLibrary, useEmbeddedNativeLibs, usesNonSdkApis, useEmbeddedDex, hasNoCode bool) android.Path { + isLibrary, useEmbeddedNativeLibs, usesNonSdkApis, useEmbeddedDex, hasNoCode bool, loggingParent string) android.Path { var args []string if isLibrary { @@ -91,6 +91,9 @@ func manifestFixer(ctx android.ModuleContext, manifest android.Path, sdkContext args = append(args, "--has-no-code") } + if loggingParent != "" { + args = append(args, "--logging-parent", loggingParent) + } var deps android.Paths targetSdkVersion, err := sdkContext.targetSdkVersion().effectiveVersionString(ctx) if err != nil { diff --git a/java/app.go b/java/app.go index 9503ec455..71bad683c 100755 --- a/java/app.go +++ b/java/app.go @@ -111,6 +111,9 @@ type overridableAppProperties struct { // the package name of this app. The package name in the manifest file is used if one was not given. Package_name *string + + // the logging parent of this app. + Logging_parent *string } type AndroidApp struct { @@ -142,6 +145,10 @@ type AndroidApp struct { noticeOutputs android.NoticeOutputs } +func (a *AndroidApp) IsInstallable() bool { + return Bool(a.properties.Installable) +} + func (a *AndroidApp) ExportedProguardFlagFiles() android.Paths { return nil } @@ -269,13 +276,7 @@ func (a *AndroidApp) aaptBuildActions(ctx android.ModuleContext) { aaptLinkFlags := []string{} // Add TARGET_AAPT_CHARACTERISTICS values to AAPT link flags if they exist and --product flags were not provided. - hasProduct := false - for _, f := range a.aaptProperties.Aaptflags { - if strings.HasPrefix(f, "--product") { - hasProduct = true - break - } - } + hasProduct := android.PrefixInList(a.aaptProperties.Aaptflags, "--product") if !hasProduct && len(ctx.Config().ProductAAPTCharacteristics()) > 0 { aaptLinkFlags = append(aaptLinkFlags, "--product", ctx.Config().ProductAAPTCharacteristics()) } @@ -305,7 +306,7 @@ func (a *AndroidApp) aaptBuildActions(ctx android.ModuleContext) { a.aapt.splitNames = a.appProperties.Package_splits a.aapt.sdkLibraries = a.exportedSdkLibs - + a.aapt.LoggingParent = String(a.overridableAppProperties.Logging_parent) a.aapt.buildActions(ctx, sdkContext(a), aaptLinkFlags...) // apps manifests are handled by aapt, don't let Module see them @@ -338,7 +339,6 @@ func (a *AndroidApp) dexBuildActions(ctx android.ModuleContext) android.Path { installDir = filepath.Join("app", a.installApkName) } a.dexpreopter.installPath = android.PathForModuleInstall(ctx, installDir, a.installApkName+".apk") - a.dexpreopter.isInstallable = Bool(a.properties.Installable) a.dexpreopter.uncompressedDex = a.shouldUncompressDex(ctx) a.dexpreopter.enforceUsesLibs = a.usesLibrary.enforceUsesLibraries() @@ -922,6 +922,10 @@ type AndroidAppImportProperties struct { Filename *string } +func (a *AndroidAppImport) IsInstallable() bool { + return true +} + // Updates properties with variant-specific values. func (a *AndroidAppImport) processVariants(ctx android.LoadHookContext) { config := ctx.Config() @@ -1064,7 +1068,6 @@ func (a *AndroidAppImport) generateAndroidBuildActions(ctx android.ModuleContext } a.dexpreopter.installPath = installDir.Join(ctx, a.BaseModuleName()+".apk") - a.dexpreopter.isInstallable = true a.dexpreopter.isPresignedPrebuilt = Bool(a.properties.Presigned) a.dexpreopter.uncompressedDex = a.shouldUncompressDex(ctx) diff --git a/java/app_test.go b/java/app_test.go index c86b038af..6d94160fa 100644 --- a/java/app_test.go +++ b/java/app_test.go @@ -1181,6 +1181,7 @@ func TestOverrideAndroidApp(t *testing.T) { name: "bar", base: "foo", certificate: ":new_certificate", + logging_parent: "bah", } android_app_certificate { @@ -1196,37 +1197,41 @@ func TestOverrideAndroidApp(t *testing.T) { `) expectedVariants := []struct { - moduleName string - variantName string - apkName string - apkPath string - signFlag string - overrides []string - aaptFlag string + moduleName string + variantName string + apkName string + apkPath string + signFlag string + overrides []string + aaptFlag string + logging_parent string }{ { - moduleName: "foo", - variantName: "android_common", - apkPath: "/target/product/test_device/system/app/foo/foo.apk", - signFlag: "build/make/target/product/security/expiredkey.x509.pem build/make/target/product/security/expiredkey.pk8", - overrides: []string{"qux"}, - aaptFlag: "", + moduleName: "foo", + variantName: "android_common", + apkPath: "/target/product/test_device/system/app/foo/foo.apk", + signFlag: "build/make/target/product/security/expiredkey.x509.pem build/make/target/product/security/expiredkey.pk8", + overrides: []string{"qux"}, + aaptFlag: "", + logging_parent: "", }, { - moduleName: "bar", - variantName: "android_common_bar", - apkPath: "/target/product/test_device/system/app/bar/bar.apk", - signFlag: "cert/new_cert.x509.pem cert/new_cert.pk8", - overrides: []string{"qux", "foo"}, - aaptFlag: "", + moduleName: "bar", + variantName: "android_common_bar", + apkPath: "/target/product/test_device/system/app/bar/bar.apk", + signFlag: "cert/new_cert.x509.pem cert/new_cert.pk8", + overrides: []string{"qux", "foo"}, + aaptFlag: "", + logging_parent: "bah", }, { - moduleName: "baz", - variantName: "android_common_baz", - apkPath: "/target/product/test_device/system/app/baz/baz.apk", - signFlag: "build/make/target/product/security/expiredkey.x509.pem build/make/target/product/security/expiredkey.pk8", - overrides: []string{"qux", "foo"}, - aaptFlag: "--rename-manifest-package org.dandroid.bp", + moduleName: "baz", + variantName: "android_common_baz", + apkPath: "/target/product/test_device/system/app/baz/baz.apk", + signFlag: "build/make/target/product/security/expiredkey.x509.pem build/make/target/product/security/expiredkey.pk8", + overrides: []string{"qux", "foo"}, + aaptFlag: "--rename-manifest-package org.dandroid.bp", + logging_parent: "", }, } for _, expected := range expectedVariants { @@ -1260,6 +1265,13 @@ func TestOverrideAndroidApp(t *testing.T) { expected.overrides, mod.appProperties.Overrides) } + // Test Overridable property: Logging_parent + logging_parent := mod.aapt.LoggingParent + if expected.logging_parent != logging_parent { + t.Errorf("Incorrect overrides property value for logging parent, expected: %q, got: %q", + expected.logging_parent, logging_parent) + } + // Check the package renaming flag, if exists. res := variant.Output("package-res.apk") aapt2Flags := res.Args["flags"] diff --git a/java/dexpreopt.go b/java/dexpreopt.go index c81e199c6..4313964aa 100644 --- a/java/dexpreopt.go +++ b/java/dexpreopt.go @@ -19,6 +19,11 @@ import ( "android/soong/dexpreopt" ) +type dexpreopterInterface interface { + IsInstallable() bool // Structs that embed dexpreopter must implement this. + dexpreoptDisabled(ctx android.BaseModuleContext) bool +} + type dexpreopter struct { dexpreoptProperties DexpreoptProperties @@ -26,7 +31,6 @@ type dexpreopter struct { uncompressedDex bool isSDKLibrary bool isTest bool - isInstallable bool isPresignedPrebuilt bool manifestFile android.Path @@ -58,8 +62,8 @@ type DexpreoptProperties struct { } } -func (d *dexpreopter) dexpreoptDisabled(ctx android.ModuleContext) bool { - global := dexpreoptGlobalConfig(ctx) +func (d *dexpreopter) dexpreoptDisabled(ctx android.BaseModuleContext) bool { + global := dexpreopt.GetGlobalConfig(ctx) if global.DisablePreopt { return true @@ -81,7 +85,11 @@ func (d *dexpreopter) dexpreoptDisabled(ctx android.ModuleContext) bool { return true } - if !d.isInstallable { + if !ctx.Module().(dexpreopterInterface).IsInstallable() { + return true + } + + if ctx.Host() { return true } @@ -95,16 +103,28 @@ func (d *dexpreopter) dexpreoptDisabled(ctx android.ModuleContext) bool { return false } +func dexpreoptToolDepsMutator(ctx android.BottomUpMutatorContext) { + if d, ok := ctx.Module().(dexpreopterInterface); !ok || d.dexpreoptDisabled(ctx) { + return + } + dexpreopt.RegisterToolDeps(ctx) +} + func odexOnSystemOther(ctx android.ModuleContext, installPath android.InstallPath) bool { - return dexpreopt.OdexOnSystemOtherByName(ctx.ModuleName(), android.InstallPathToOnDevicePath(ctx, installPath), dexpreoptGlobalConfig(ctx)) + return dexpreopt.OdexOnSystemOtherByName(ctx.ModuleName(), android.InstallPathToOnDevicePath(ctx, installPath), dexpreopt.GetGlobalConfig(ctx)) } func (d *dexpreopter) dexpreopt(ctx android.ModuleContext, dexJarFile android.ModuleOutPath) android.ModuleOutPath { - if d.dexpreoptDisabled(ctx) { + // TODO(b/148690468): The check on d.installPath is to bail out in cases where + // the dexpreopter struct hasn't been fully initialized before we're called, + // e.g. in aar.go. This keeps the behaviour that dexpreopting is effectively + // disabled, even if installable is true. + if d.dexpreoptDisabled(ctx) || d.installPath.Base() == "." { return dexJarFile } - global := dexpreoptGlobalConfig(ctx) + globalSoong := dexpreopt.GetGlobalSoongConfig(ctx) + global := dexpreopt.GetGlobalConfig(ctx) bootImage := defaultBootImageConfig(ctx) dexFiles := bootImage.dexPathsDeps.Paths() dexLocations := bootImage.dexLocationsDeps @@ -156,7 +176,7 @@ func (d *dexpreopter) dexpreopt(ctx android.ModuleContext, dexJarFile android.Mo } } - dexpreoptConfig := dexpreopt.ModuleConfig{ + dexpreoptConfig := &dexpreopt.ModuleConfig{ Name: ctx.ModuleName(), DexLocation: dexLocation, BuildPath: android.PathForModuleOut(ctx, "dexpreopt", ctx.ModuleName()+".jar").OutputPath, @@ -191,7 +211,7 @@ func (d *dexpreopter) dexpreopt(ctx android.ModuleContext, dexJarFile android.Mo PresignedPrebuilt: d.isPresignedPrebuilt, } - dexpreoptRule, err := dexpreopt.GenerateDexpreoptRule(ctx, global, dexpreoptConfig) + dexpreoptRule, err := dexpreopt.GenerateDexpreoptRule(ctx, globalSoong, global, dexpreoptConfig) if err != nil { ctx.ModuleErrorf("error generating dexpreopt rule: %s", err.Error()) return dexJarFile diff --git a/java/dexpreopt_bootjars.go b/java/dexpreopt_bootjars.go index 607a43781..655a47644 100644 --- a/java/dexpreopt_bootjars.go +++ b/java/dexpreopt_bootjars.go @@ -165,7 +165,7 @@ func dexpreoptBootJarsFactory() android.Singleton { } func skipDexpreoptBootJars(ctx android.PathContext) bool { - if dexpreoptGlobalConfig(ctx).DisablePreopt { + if dexpreopt.GetGlobalConfig(ctx).DisablePreopt { return true } @@ -205,11 +205,15 @@ func (d *dexpreoptBootJars) GenerateBuildActions(ctx android.SingletonContext) { if skipDexpreoptBootJars(ctx) { return } + if dexpreopt.GetCachedGlobalSoongConfig(ctx) == nil { + // No module has enabled dexpreopting, so we assume there will be no boot image to make. + return + } d.dexpreoptConfigForMake = android.PathForOutput(ctx, ctx.Config().DeviceName(), "dexpreopt.config") writeGlobalConfigForMake(ctx, d.dexpreoptConfigForMake) - global := dexpreoptGlobalConfig(ctx) + global := dexpreopt.GetGlobalConfig(ctx) // Skip recompiling the boot image for the second sanitization phase. We'll get separate paths // and invalidate first-stage artifacts which are crucial to SANITIZE_LITE builds. @@ -295,7 +299,8 @@ func buildBootImage(ctx android.SingletonContext, config bootImageConfig) *bootI func buildBootImageRuleForArch(ctx android.SingletonContext, image *bootImage, arch android.ArchType, profile android.Path, missingDeps []string) android.WritablePaths { - global := dexpreoptGlobalConfig(ctx) + globalSoong := dexpreopt.GetCachedGlobalSoongConfig(ctx) + global := dexpreopt.GetGlobalConfig(ctx) symbolsDir := image.symbolsDir.Join(ctx, image.installSubdir, arch.String()) symbolsFile := symbolsDir.Join(ctx, image.stem+".oat") @@ -330,7 +335,7 @@ func buildBootImageRuleForArch(ctx android.SingletonContext, image *bootImage, invocationPath := outputPath.ReplaceExtension(ctx, "invocation") - cmd.Tool(global.SoongConfig.Dex2oat). + cmd.Tool(globalSoong.Dex2oat). Flag("--avoid-storing-invocation"). FlagWithOutput("--write-invocation-to=", invocationPath).ImplicitOutput(invocationPath). Flag("--runtime-arg").FlagWithArg("-Xms", global.Dex2oatImageXms). @@ -433,7 +438,8 @@ It is likely that the boot classpath is inconsistent. Rebuild with ART_BOOT_IMAGE_EXTRA_ARGS="--runtime-arg -verbose:verifier" to see verification errors.` func bootImageProfileRule(ctx android.SingletonContext, image *bootImage, missingDeps []string) android.WritablePath { - global := dexpreoptGlobalConfig(ctx) + globalSoong := dexpreopt.GetCachedGlobalSoongConfig(ctx) + global := dexpreopt.GetGlobalConfig(ctx) if global.DisableGenerateProfile || ctx.Config().IsPdkBuild() || ctx.Config().UnbundledBuild() { return nil @@ -464,7 +470,7 @@ func bootImageProfileRule(ctx android.SingletonContext, image *bootImage, missin rule.Command(). Text(`ANDROID_LOG_TAGS="*:e"`). - Tool(global.SoongConfig.Profman). + Tool(globalSoong.Profman). FlagWithInput("--create-profile-from=", bootImageProfile). FlagForEachInput("--apk=", image.dexPathsDeps.Paths()). FlagForEachArg("--dex-location=", image.dexLocationsDeps). @@ -487,7 +493,8 @@ func bootImageProfileRule(ctx android.SingletonContext, image *bootImage, missin var bootImageProfileRuleKey = android.NewOnceKey("bootImageProfileRule") func bootFrameworkProfileRule(ctx android.SingletonContext, image *bootImage, missingDeps []string) android.WritablePath { - global := dexpreoptGlobalConfig(ctx) + globalSoong := dexpreopt.GetCachedGlobalSoongConfig(ctx) + global := dexpreopt.GetGlobalConfig(ctx) if global.DisableGenerateProfile || ctx.Config().IsPdkBuild() || ctx.Config().UnbundledBuild() { return nil @@ -513,7 +520,7 @@ func bootFrameworkProfileRule(ctx android.SingletonContext, image *bootImage, mi rule.Command(). Text(`ANDROID_LOG_TAGS="*:e"`). - Tool(global.SoongConfig.Profman). + Tool(globalSoong.Profman). Flag("--generate-boot-profile"). FlagWithInput("--create-profile-from=", bootFrameworkProfile). FlagForEachInput("--apk=", image.dexPathsDeps.Paths()). @@ -575,7 +582,7 @@ func dumpOatRules(ctx android.SingletonContext, image *bootImage) { } func writeGlobalConfigForMake(ctx android.SingletonContext, path android.WritablePath) { - data := dexpreoptGlobalConfigRaw(ctx).data + data := dexpreopt.GetGlobalConfigRawData(ctx) ctx.Build(pctx, android.BuildParams{ Rule: android.WriteFile, diff --git a/java/dexpreopt_bootjars_test.go b/java/dexpreopt_bootjars_test.go index 4ce30f678..c3b2133a9 100644 --- a/java/dexpreopt_bootjars_test.go +++ b/java/dexpreopt_bootjars_test.go @@ -49,7 +49,7 @@ func TestDexpreoptBootJars(t *testing.T) { pathCtx := android.PathContextForTesting(config) dexpreoptConfig := dexpreopt.GlobalConfigForTests(pathCtx) dexpreoptConfig.BootJars = []string{"foo", "bar", "baz"} - setDexpreoptTestGlobalConfig(config, dexpreoptConfig) + dexpreopt.SetTestGlobalConfig(config, dexpreoptConfig) ctx := testContext() diff --git a/java/dexpreopt_config.go b/java/dexpreopt_config.go index 4c9add8b5..28f56d24a 100644 --- a/java/dexpreopt_config.go +++ b/java/dexpreopt_config.go @@ -15,6 +15,7 @@ package java import ( + "fmt" "path/filepath" "strings" @@ -22,67 +23,36 @@ import ( "android/soong/dexpreopt" ) -// dexpreoptGlobalConfig returns the global dexpreopt.config. It is loaded once the first time it is called for any -// ctx.Config(), and returns the same data for all future calls with the same ctx.Config(). A value can be inserted -// for tests using setDexpreoptTestGlobalConfig. -func dexpreoptGlobalConfig(ctx android.PathContext) dexpreopt.GlobalConfig { - return dexpreoptGlobalConfigRaw(ctx).global -} - -type globalConfigAndRaw struct { - global dexpreopt.GlobalConfig - data []byte -} - -func dexpreoptGlobalConfigRaw(ctx android.PathContext) globalConfigAndRaw { - return ctx.Config().Once(dexpreoptGlobalConfigKey, func() interface{} { - if data, err := ctx.Config().DexpreoptGlobalConfig(ctx); err != nil { - panic(err) - } else if data != nil { - soongConfig := dexpreopt.CreateGlobalSoongConfig(ctx) - globalConfig, err := dexpreopt.LoadGlobalConfig(ctx, data, soongConfig) - if err != nil { - panic(err) - } - return globalConfigAndRaw{globalConfig, data} - } - - // No global config filename set, see if there is a test config set - return ctx.Config().Once(dexpreoptTestGlobalConfigKey, func() interface{} { - // Nope, return a config with preopting disabled - return globalConfigAndRaw{dexpreopt.GlobalConfig{ - DisablePreopt: true, - DisableGenerateProfile: true, - }, nil} - }) - }).(globalConfigAndRaw) -} - -// setDexpreoptTestGlobalConfig sets a GlobalConfig that future calls to dexpreoptGlobalConfig will return. It must -// be called before the first call to dexpreoptGlobalConfig for the config. -func setDexpreoptTestGlobalConfig(config android.Config, globalConfig dexpreopt.GlobalConfig) { - config.Once(dexpreoptTestGlobalConfigKey, func() interface{} { return globalConfigAndRaw{globalConfig, nil} }) -} - -var dexpreoptGlobalConfigKey = android.NewOnceKey("DexpreoptGlobalConfig") -var dexpreoptTestGlobalConfigKey = android.NewOnceKey("TestDexpreoptGlobalConfig") - // systemServerClasspath returns the on-device locations of the modules in the system server classpath. It is computed // once the first time it is called for any ctx.Config(), and returns the same slice for all future calls with the same // ctx.Config(). -func systemServerClasspath(ctx android.PathContext) []string { +func systemServerClasspath(ctx android.MakeVarsContext) []string { return ctx.Config().OnceStringSlice(systemServerClasspathKey, func() []string { - global := dexpreoptGlobalConfig(ctx) - + global := dexpreopt.GetGlobalConfig(ctx) var systemServerClasspathLocations []string - for _, m := range global.SystemServerJars { + var dexpreoptJars = *DexpreoptedSystemServerJars(ctx.Config()) + // 1) The jars that are dexpreopted. + for _, m := range dexpreoptJars { systemServerClasspathLocations = append(systemServerClasspathLocations, filepath.Join("/system/framework", m+".jar")) } + // 2) The jars that are from an updatable apex. for _, m := range global.UpdatableSystemServerJars { systemServerClasspathLocations = append(systemServerClasspathLocations, dexpreopt.GetJarLocationFromApexJarPair(m)) } + // 3) The jars from make (which are not updatable, not preopted). + for _, m := range dexpreopt.NonUpdatableSystemServerJars(ctx, global) { + if !android.InList(m, dexpreoptJars) { + systemServerClasspathLocations = append(systemServerClasspathLocations, + filepath.Join("/system/framework", m+".jar")) + } + } + if len(systemServerClasspathLocations) != len(global.SystemServerJars)+len(global.UpdatableSystemServerJars) { + panic(fmt.Errorf("Wrong number of system server jars, got %d, expected %d", + len(systemServerClasspathLocations), + len(global.SystemServerJars)+len(global.UpdatableSystemServerJars))) + } return systemServerClasspathLocations }) } @@ -112,26 +82,17 @@ func stemOf(moduleName string) string { return moduleName } -func getJarsFromApexJarPairs(apexJarPairs []string) []string { - modules := make([]string, len(apexJarPairs)) - for i, p := range apexJarPairs { - _, jar := dexpreopt.SplitApexJarPair(p) - modules[i] = jar - } - return modules -} - var ( - bootImageConfigKey = android.NewOnceKey("bootImageConfig") - artBootImageName = "art" - frameworkBootImageName = "boot" + bootImageConfigKey = android.NewOnceKey("bootImageConfig") + artBootImageName = "art" + frameworkBootImageName = "boot" ) // Construct the global boot image configs. func genBootImageConfigs(ctx android.PathContext) map[string]*bootImageConfig { return ctx.Config().Once(bootImageConfigKey, func() interface{} { - global := dexpreoptGlobalConfig(ctx) + global := dexpreopt.GetGlobalConfig(ctx) targets := dexpreoptTargets(ctx) deviceDir := android.PathForOutput(ctx, ctx.Config().DeviceName()) @@ -141,7 +102,7 @@ func genBootImageConfigs(ctx android.PathContext) map[string]*bootImageConfig { artModules = append(artModules, "jacocoagent") } frameworkModules := android.RemoveListFromList(global.BootJars, - concat(artModules, getJarsFromApexJarPairs(global.UpdatableBootJars))) + concat(artModules, dexpreopt.GetJarsFromApexJarPairs(global.UpdatableBootJars))) artSubdir := "apex/com.android.art/javalib" frameworkSubdir := "system/framework" @@ -179,8 +140,8 @@ func genBootImageConfigs(ctx android.PathContext) map[string]*bootImageConfig { } configs := map[string]*bootImageConfig{ - artBootImageName: &artCfg, - frameworkBootImageName: &frameworkCfg, + artBootImageName: &artCfg, + frameworkBootImageName: &frameworkCfg, } // common to all configs @@ -237,7 +198,7 @@ func defaultBootImageConfig(ctx android.PathContext) bootImageConfig { func defaultBootclasspath(ctx android.PathContext) []string { return ctx.Config().OnceStringSlice(defaultBootclasspathKey, func() []string { - global := dexpreoptGlobalConfig(ctx) + global := dexpreopt.GetGlobalConfig(ctx) image := defaultBootImageConfig(ctx) updatableBootclasspath := make([]string, len(global.UpdatableBootJars)) diff --git a/java/droiddoc.go b/java/droiddoc.go index 959f1c734..fd4b90df3 100644 --- a/java/droiddoc.go +++ b/java/droiddoc.go @@ -547,10 +547,10 @@ func (j *Javadoc) collectDeps(ctx android.ModuleContext) deps { case bootClasspathTag: if dep, ok := module.(Dependency); ok { deps.bootClasspath = append(deps.bootClasspath, dep.ImplementationJars()...) - } else if sm, ok := module.(*SystemModules); ok { + } else if sm, ok := module.(SystemModulesProvider); ok { // A system modules dependency has been added to the bootclasspath // so add its libs to the bootclasspath. - deps.bootClasspath = append(deps.bootClasspath, sm.headerJars...) + deps.bootClasspath = append(deps.bootClasspath, sm.HeaderJars()...) } else { panic(fmt.Errorf("unknown dependency %q for %q", otherName, ctx.ModuleName())) } @@ -578,11 +578,9 @@ func (j *Javadoc) collectDeps(ctx android.ModuleContext) deps { if deps.systemModules != nil { panic("Found two system module dependencies") } - sm := module.(*SystemModules) - if sm.outputDir == nil && len(sm.outputDeps) == 0 { - panic("Missing directory for system module dependency") - } - deps.systemModules = &systemModules{sm.outputDir, sm.outputDeps} + sm := module.(SystemModulesProvider) + outputDir, outputDeps := sm.OutputDirAndDeps() + deps.systemModules = &systemModules{outputDir, outputDeps} } }) // do not pass exclude_srcs directly when expanding srcFiles since exclude_srcs @@ -605,11 +603,8 @@ func (j *Javadoc) collectDeps(ctx android.ModuleContext) deps { continue } packageName := strings.ReplaceAll(filepath.Dir(src.Rel()), "/", ".") - for _, pkg := range filterPackages { - if strings.HasPrefix(packageName, pkg) { - filtered = append(filtered, src) - break - } + if android.HasAnyPrefix(packageName, filterPackages) { + filtered = append(filtered, src) } } return filtered diff --git a/java/java.go b/java/java.go index 279d67424..462dba809 100644 --- a/java/java.go +++ b/java/java.go @@ -23,12 +23,14 @@ import ( "path/filepath" "strconv" "strings" + "sync" "github.com/google/blueprint" "github.com/google/blueprint/pathtools" "github.com/google/blueprint/proptools" "android/soong/android" + "android/soong/dexpreopt" "android/soong/java/config" "android/soong/tradefed" ) @@ -52,6 +54,8 @@ func init() { PropertyName: "java_tests", }, }) + + android.PostDepsMutators(RegisterPostDepsMutators) } func RegisterJavaBuildComponents(ctx android.RegistrationContext) { @@ -72,10 +76,52 @@ func RegisterJavaBuildComponents(ctx android.RegistrationContext) { ctx.RegisterModuleType("java_host_for_device", HostForDeviceFactory) ctx.RegisterModuleType("dex_import", DexImportFactory) + ctx.FinalDepsMutators(func(ctx android.RegisterMutatorsContext) { + ctx.BottomUp("dexpreopt_tool_deps", dexpreoptToolDepsMutator).Parallel() + }) + ctx.RegisterSingletonType("logtags", LogtagsSingleton) ctx.RegisterSingletonType("kythe_java_extract", kytheExtractJavaFactory) } +func RegisterPostDepsMutators(ctx android.RegisterMutatorsContext) { + ctx.BottomUp("ordered_system_server_jars", systemServerJarsDepsMutator) +} + +var ( + dexpreoptedSystemServerJarsKey = android.NewOnceKey("dexpreoptedSystemServerJars") + dexpreoptedSystemServerJarsLock sync.Mutex +) + +func DexpreoptedSystemServerJars(config android.Config) *[]string { + return config.Once(dexpreoptedSystemServerJarsKey, func() interface{} { + return &[]string{} + }).(*[]string) +} + +// A PostDepsMutator pass that enforces total order on non-updatable system server jars. A total +// order is neededed because such jars must be dexpreopted together (each jar on the list must have +// all preceding jars in its class loader context). The total order must be compatible with the +// partial order imposed by genuine dependencies between system server jars (which is not always +// respected by the PRODUCT_SYSTEM_SERVER_JARS variable). +// +// An earlier mutator pass creates genuine dependencies, and this pass traverses the jars in that +// order (which is partial and non-deterministic). This pass adds additional dependencies between +// jars, making the order total and deterministic. It also constructs a global ordered list. +func systemServerJarsDepsMutator(ctx android.BottomUpMutatorContext) { + jars := dexpreopt.NonUpdatableSystemServerJars(ctx, dexpreopt.GetGlobalConfig(ctx)) + name := ctx.ModuleName() + if android.InList(name, jars) { + dexpreoptedSystemServerJarsLock.Lock() + defer dexpreoptedSystemServerJarsLock.Unlock() + jars := DexpreoptedSystemServerJars(ctx.Config()) + for _, dep := range *jars { + ctx.AddDependency(ctx.Module(), dexpreopt.SystemServerDepTag, dep) + } + *jars = append(*jars, name) + } +} + func (j *Module) checkSdkVersion(ctx android.ModuleContext) { if j.SocSpecific() || j.DeviceSpecific() || (j.ProductSpecific() && ctx.Config().EnforceProductPartitionInterface()) { @@ -659,6 +705,11 @@ func (j *Module) deps(ctx android.BottomUpMutatorContext) { } else if j.shouldInstrumentStatic(ctx) { ctx.AddVariationDependencies(nil, staticLibTag, "jacocoagent") } + + // services depend on com.android.location.provider, but dependency in not registered in a Blueprint file + if ctx.ModuleName() == "services" { + ctx.AddDependency(ctx.Module(), dexpreopt.SystemServerForcedDepTag, "com.android.location.provider") + } } func hasSrcExt(srcs []string, ext string) bool { @@ -756,6 +807,7 @@ const ( javaSdk javaSystem javaModule + javaSystemServer javaPlatform ) @@ -789,6 +841,10 @@ func (m *Module) getLinkType(name string) (ret linkType, stubs bool) { return javaModule, true case ver.kind == sdkModule: return javaModule, false + case name == "services-stubs": + return javaSystemServer, true + case ver.kind == sdkSystemServer: + return javaSystemServer, false case ver.kind == sdkPrivate || ver.kind == sdkNone || ver.kind == sdkCorePlatform: return javaPlatform, false case !ver.valid(): @@ -824,17 +880,23 @@ func checkLinkType(ctx android.ModuleContext, from *Module, to linkTypeContext, } break case javaSystem: - if otherLinkType == javaPlatform || otherLinkType == javaModule { + if otherLinkType == javaPlatform || otherLinkType == javaModule || otherLinkType == javaSystemServer { ctx.ModuleErrorf("compiles against system API, but dependency %q is compiling against private API."+commonMessage, ctx.OtherModuleName(to)) } break case javaModule: - if otherLinkType == javaPlatform { + if otherLinkType == javaPlatform || otherLinkType == javaSystemServer { ctx.ModuleErrorf("compiles against module API, but dependency %q is compiling against private API."+commonMessage, ctx.OtherModuleName(to)) } break + case javaSystemServer: + if otherLinkType == javaPlatform { + ctx.ModuleErrorf("compiles against system server API, but dependency %q is compiling against private API."+commonMessage, + ctx.OtherModuleName(to)) + } + break case javaPlatform: // no restriction on link-type break @@ -969,18 +1031,16 @@ func (j *Module) collectDeps(ctx android.ModuleContext) deps { case bootClasspathTag: // If a system modules dependency has been added to the bootclasspath // then add its libs to the bootclasspath. - sm := module.(*SystemModules) - deps.bootClasspath = append(deps.bootClasspath, sm.headerJars...) + sm := module.(SystemModulesProvider) + deps.bootClasspath = append(deps.bootClasspath, sm.HeaderJars()...) case systemModulesTag: if deps.systemModules != nil { panic("Found two system module dependencies") } - sm := module.(*SystemModules) - if sm.outputDir == nil || len(sm.outputDeps) == 0 { - panic("Missing directory for system module dependency") - } - deps.systemModules = &systemModules{sm.outputDir, sm.outputDeps} + sm := module.(SystemModulesProvider) + outputDir, outputDeps := sm.OutputDirAndDeps() + deps.systemModules = &systemModules{outputDir, outputDeps} } } }) @@ -1734,6 +1794,10 @@ func (j *Module) JacocoReportClassesFile() android.Path { return j.jacocoReportClassesFile } +func (j *Module) IsInstallable() bool { + return Bool(j.properties.Installable) +} + // // Java libraries (.jar file) // @@ -1771,7 +1835,6 @@ func (j *Library) GenerateAndroidBuildActions(ctx android.ModuleContext) { j.checkSdkVersion(ctx) j.dexpreopter.installPath = android.PathForModuleInstall(ctx, "framework", j.Stem()+".jar") j.dexpreopter.isSDKLibrary = j.deviceProperties.IsSDKLibrary - j.dexpreopter.isInstallable = Bool(j.properties.Installable) j.dexpreopter.uncompressedDex = shouldUncompressDex(ctx, &j.dexpreopter) j.deviceProperties.UncompressDex = j.dexpreopter.uncompressedDex j.compile(ctx, nil) @@ -2528,13 +2591,16 @@ func (j *DexImport) Stem() string { return proptools.StringDefault(j.properties.Stem, j.ModuleBase.Name()) } +func (j *DexImport) IsInstallable() bool { + return true +} + func (j *DexImport) GenerateAndroidBuildActions(ctx android.ModuleContext) { if len(j.properties.Jars) != 1 { ctx.PropertyErrorf("jars", "exactly one jar must be provided") } j.dexpreopter.installPath = android.PathForModuleInstall(ctx, "framework", j.Stem()+".jar") - j.dexpreopter.isInstallable = true j.dexpreopter.uncompressedDex = shouldUncompressDex(ctx, &j.dexpreopter) inputJar := ctx.ExpandSource(j.properties.Jars[0], "jars") diff --git a/java/java_test.go b/java/java_test.go index 17921ca44..52b3bc087 100644 --- a/java/java_test.go +++ b/java/java_test.go @@ -57,7 +57,15 @@ func TestMain(m *testing.M) { } func testConfig(env map[string]string, bp string, fs map[string][]byte) android.Config { - return TestConfig(buildDir, env, bp, fs) + bp += dexpreopt.BpToolModulesForTest() + + config := TestConfig(buildDir, env, bp, fs) + + // Set up the global Once cache used for dexpreopt.GlobalSoongConfig, so that + // it doesn't create a real one, which would fail. + _ = dexpreopt.GlobalSoongConfigForTests(config) + + return config } func testContext() *android.TestContext { @@ -86,6 +94,8 @@ func testContext() *android.TestContext { cc.RegisterRequiredBuildComponentsForTest(ctx) ctx.RegisterModuleType("ndk_prebuilt_shared_stl", cc.NdkPrebuiltSharedStlFactory) + dexpreopt.RegisterToolModulesForTest(ctx) + return ctx } @@ -93,7 +103,7 @@ func run(t *testing.T, ctx *android.TestContext, config android.Config) { t.Helper() pathCtx := android.PathContextForTesting(config) - setDexpreoptTestGlobalConfig(config, dexpreopt.GlobalConfigForTests(pathCtx)) + dexpreopt.SetTestGlobalConfig(config, dexpreopt.GlobalConfigForTests(pathCtx)) ctx.Register(config) _, errs := ctx.ParseBlueprintsFiles("Android.bp") @@ -112,7 +122,7 @@ func testJavaErrorWithConfig(t *testing.T, pattern string, config android.Config ctx := testContext() pathCtx := android.PathContextForTesting(config) - setDexpreoptTestGlobalConfig(config, dexpreopt.GlobalConfigForTests(pathCtx)) + dexpreopt.SetTestGlobalConfig(config, dexpreopt.GlobalConfigForTests(pathCtx)) ctx.Register(config) _, errs := ctx.ParseBlueprintsFiles("Android.bp") @@ -972,6 +982,65 @@ func TestDroiddoc(t *testing.T) { } } +func TestDroidstubsWithSystemModules(t *testing.T) { + ctx, _ := testJava(t, ` + droidstubs { + name: "stubs-source-system-modules", + srcs: [ + "bar-doc/*.java", + ], + sdk_version: "none", + system_modules: "source-system-modules", + } + + java_library { + name: "source-jar", + srcs: [ + "a.java", + ], + } + + java_system_modules { + name: "source-system-modules", + libs: ["source-jar"], + } + + droidstubs { + name: "stubs-prebuilt-system-modules", + srcs: [ + "bar-doc/*.java", + ], + sdk_version: "none", + system_modules: "prebuilt-system-modules", + } + + java_import { + name: "prebuilt-jar", + jars: ["a.jar"], + } + + java_system_modules_import { + name: "prebuilt-system-modules", + libs: ["prebuilt-jar"], + } + `) + + checkSystemModulesUseByDroidstubs(t, ctx, "stubs-source-system-modules", "source-jar.jar") + + checkSystemModulesUseByDroidstubs(t, ctx, "stubs-prebuilt-system-modules", "prebuilt-jar.jar") +} + +func checkSystemModulesUseByDroidstubs(t *testing.T, ctx *android.TestContext, moduleName string, systemJar string) { + metalavaRule := ctx.ModuleForTests(moduleName, "android_common").Rule("metalava") + var systemJars []string + for _, i := range metalavaRule.Implicits { + systemJars = append(systemJars, i.Base()) + } + if len(systemJars) != 1 || systemJars[0] != systemJar { + t.Errorf("inputs of %q must be []string{%q}, but was %#v.", moduleName, systemJar, systemJars) + } +} + func TestJarGenrules(t *testing.T) { ctx, _ := testJava(t, ` java_library { @@ -1367,3 +1436,59 @@ func TestJavaSystemModulesImport(t *testing.T) { } } } + +func TestJavaLibraryWithSystemModules(t *testing.T) { + ctx, _ := testJava(t, ` + java_library { + name: "lib-with-source-system-modules", + srcs: [ + "a.java", + ], + sdk_version: "none", + system_modules: "source-system-modules", + } + + java_library { + name: "source-jar", + srcs: [ + "a.java", + ], + } + + java_system_modules { + name: "source-system-modules", + libs: ["source-jar"], + } + + java_library { + name: "lib-with-prebuilt-system-modules", + srcs: [ + "a.java", + ], + sdk_version: "none", + system_modules: "prebuilt-system-modules", + } + + java_import { + name: "prebuilt-jar", + jars: ["a.jar"], + } + + java_system_modules_import { + name: "prebuilt-system-modules", + libs: ["prebuilt-jar"], + } + `) + + checkBootClasspathForSystemModule(t, ctx, "lib-with-source-system-modules", "/source-jar.jar") + + checkBootClasspathForSystemModule(t, ctx, "lib-with-prebuilt-system-modules", "/prebuilt-jar.jar") +} + +func checkBootClasspathForSystemModule(t *testing.T, ctx *android.TestContext, moduleName string, expectedSuffix string) { + javacRule := ctx.ModuleForTests(moduleName, "android_common").Rule("javac") + bootClasspath := javacRule.Args["bootClasspath"] + if strings.HasPrefix(bootClasspath, "--system ") && strings.HasSuffix(bootClasspath, expectedSuffix) { + t.Errorf("bootclasspath of %q must start with --system and end with %q, but was %#v.", moduleName, expectedSuffix, bootClasspath) + } +} diff --git a/java/sdk.go b/java/sdk.go index 1c047a353..1e60d6774 100644 --- a/java/sdk.go +++ b/java/sdk.go @@ -72,6 +72,7 @@ const ( sdkSystem sdkTest sdkModule + sdkSystemServer sdkPrivate ) @@ -94,6 +95,8 @@ func (k sdkKind) String() string { return "core_platform" case sdkModule: return "module" + case sdkSystemServer: + return "system_server" default: return "invalid" } @@ -261,6 +264,8 @@ func sdkSpecFrom(str string) sdkSpec { kind = sdkTest case "module": kind = sdkModule + case "system_server": + kind = sdkSystemServer default: return sdkSpec{sdkInvalid, sdkVersionNone, str} } @@ -324,13 +329,13 @@ func decodeSdkDep(ctx android.EarlyModuleContext, sdkContext sdkContext) sdkDep } } - toModule := func(m, r string, aidl android.Path) sdkDep { + toModule := func(modules []string, res string, aidl android.Path) sdkDep { return sdkDep{ useModule: true, - bootclasspath: []string{m, config.DefaultLambdaStubsLibrary}, + bootclasspath: append(modules, config.DefaultLambdaStubsLibrary), systemModules: "core-current-stubs-system-modules", - java9Classpath: []string{m}, - frameworkResModule: r, + java9Classpath: modules, + frameworkResModule: res, aidl: android.OptionalPathForPath(aidl), } } @@ -380,16 +385,19 @@ func decodeSdkDep(ctx android.EarlyModuleContext, sdkContext sdkContext) sdkDep noFrameworksLibs: true, } case sdkPublic: - return toModule("android_stubs_current", "framework-res", sdkFrameworkAidlPath(ctx)) + return toModule([]string{"android_stubs_current"}, "framework-res", sdkFrameworkAidlPath(ctx)) case sdkSystem: - return toModule("android_system_stubs_current", "framework-res", sdkFrameworkAidlPath(ctx)) + return toModule([]string{"android_system_stubs_current"}, "framework-res", sdkFrameworkAidlPath(ctx)) case sdkTest: - return toModule("android_test_stubs_current", "framework-res", sdkFrameworkAidlPath(ctx)) + return toModule([]string{"android_test_stubs_current"}, "framework-res", sdkFrameworkAidlPath(ctx)) case sdkCore: - return toModule("core.current.stubs", "", nil) + return toModule([]string{"core.current.stubs"}, "", nil) case sdkModule: // TODO(146757305): provide .apk and .aidl that have more APIs for modules - return toModule("android_module_lib_stubs_current", "framework-res", sdkFrameworkAidlPath(ctx)) + return toModule([]string{"android_module_lib_stubs_current"}, "framework-res", sdkFrameworkAidlPath(ctx)) + case sdkSystemServer: + // TODO(146757305): provide .apk and .aidl that have more APIs for modules + return toModule([]string{"android_module_lib_stubs_current", "services-stubs"}, "framework-res", sdkFrameworkAidlPath(ctx)) default: panic(fmt.Errorf("invalid sdk %q", sdkVersion.raw)) } diff --git a/java/sdk_test.go b/java/sdk_test.go index c815fe3c1..ea6733d86 100644 --- a/java/sdk_test.go +++ b/java/sdk_test.go @@ -212,7 +212,6 @@ func TestClasspath(t *testing.T) { aidl: "-pprebuilts/sdk/29/public/framework.aidl", }, { - name: "module_current", properties: `sdk_version: "module_current",`, bootclasspath: []string{"android_module_lib_stubs_current", "core-lambda-stubs"}, @@ -220,6 +219,14 @@ func TestClasspath(t *testing.T) { java9classpath: []string{"android_module_lib_stubs_current"}, aidl: "-p" + buildDir + "/framework.aidl", }, + { + name: "system_server_current", + properties: `sdk_version: "system_server_current",`, + bootclasspath: []string{"android_module_lib_stubs_current", "services-stubs", "core-lambda-stubs"}, + system: "core-current-stubs-system-modules", + java9classpath: []string{"android_module_lib_stubs_current", "services-stubs"}, + aidl: "-p" + buildDir + "/framework.aidl", + }, } for _, testcase := range classpathTestcases { diff --git a/java/system_modules.go b/java/system_modules.go index 731503fd0..47de6e327 100644 --- a/java/system_modules.go +++ b/java/system_modules.go @@ -117,6 +117,15 @@ func SystemModulesFactory() android.Module { return module } +type SystemModulesProvider interface { + HeaderJars() android.Paths + OutputDirAndDeps() (android.Path, android.Paths) +} + +var _ SystemModulesProvider = (*SystemModules)(nil) + +var _ SystemModulesProvider = (*systemModulesImport)(nil) + type SystemModules struct { android.ModuleBase android.DefaultableModuleBase @@ -136,6 +145,17 @@ type SystemModulesProperties struct { Libs []string } +func (system *SystemModules) HeaderJars() android.Paths { + return system.headerJars +} + +func (system *SystemModules) OutputDirAndDeps() (android.Path, android.Paths) { + if system.outputDir == nil || len(system.outputDeps) == 0 { + panic("Missing directory for system module dependency") + } + return system.outputDir, system.outputDeps +} + func (system *SystemModules) GenerateAndroidBuildActions(ctx android.ModuleContext) { var jars android.Paths diff --git a/java/testing.go b/java/testing.go index 8f979c73e..3111109a5 100644 --- a/java/testing.go +++ b/java/testing.go @@ -147,6 +147,7 @@ func GatherRequiredDepsForTest() string { "android_system_stubs_current", "android_test_stubs_current", "android_module_lib_stubs_current", + "services-stubs", "core.current.stubs", "core.platform.api.stubs", "kotlin-stdlib", diff --git a/scripts/manifest_fixer.py b/scripts/manifest_fixer.py index 945bc1832..c59732bb9 100755 --- a/scripts/manifest_fixer.py +++ b/scripts/manifest_fixer.py @@ -51,6 +51,9 @@ def parse_args(): help='specify additional <uses-library> tag to add. android:requred is set to false') parser.add_argument('--uses-non-sdk-api', dest='uses_non_sdk_api', action='store_true', help='manifest is for a package built against the platform') + parser.add_argument('--logging-parent', dest='logging_parent', default='', + help=('specify logging parent as an additional <meta-data> tag. ' + 'This value is ignored if the logging_parent meta-data tag is present.')) parser.add_argument('--use-embedded-dex', dest='use_embedded_dex', action='store_true', help=('specify if the app wants to use embedded dex and avoid extracted,' 'locally compiled code. Must not conflict if already declared ' @@ -124,6 +127,52 @@ def raise_min_sdk_version(doc, min_sdk_version, target_sdk_version, library): element.setAttributeNode(target_attr) +def add_logging_parent(doc, logging_parent_value): + """Add logging parent as an additional <meta-data> tag. + + Args: + doc: The XML document. May be modified by this function. + logging_parent_value: A string representing the logging + parent value. + Raises: + RuntimeError: Invalid manifest + """ + manifest = parse_manifest(doc) + + logging_parent_key = 'android.content.pm.LOGGING_PARENT' + elems = get_children_with_tag(manifest, 'application') + application = elems[0] if len(elems) == 1 else None + if len(elems) > 1: + raise RuntimeError('found multiple <application> tags') + elif not elems: + application = doc.createElement('application') + indent = get_indent(manifest.firstChild, 1) + first = manifest.firstChild + manifest.insertBefore(doc.createTextNode(indent), first) + manifest.insertBefore(application, first) + + indent = get_indent(application.firstChild, 2) + + last = application.lastChild + if last is not None and last.nodeType != minidom.Node.TEXT_NODE: + last = None + + if not find_child_with_attribute(application, 'meta-data', android_ns, + 'name', logging_parent_key): + ul = doc.createElement('meta-data') + ul.setAttributeNS(android_ns, 'android:name', logging_parent_key) + ul.setAttributeNS(android_ns, 'android:value', logging_parent_value) + application.insertBefore(doc.createTextNode(indent), last) + application.insertBefore(ul, last) + last = application.lastChild + + # align the closing tag with the opening tag if it's not + # indented + if last and last.nodeType != minidom.Node.TEXT_NODE: + indent = get_indent(application.previousSibling, 1) + application.appendChild(doc.createTextNode(indent)) + + def add_uses_libraries(doc, new_uses_libraries, required): """Add additional <uses-library> tags @@ -291,6 +340,9 @@ def main(): if args.uses_non_sdk_api: add_uses_non_sdk_api(doc) + if args.logging_parent: + add_logging_parent(doc, args.logging_parent) + if args.use_embedded_dex: add_use_embedded_dex(doc) diff --git a/scripts/manifest_fixer_test.py b/scripts/manifest_fixer_test.py index ea8095e48..d6e7f2674 100755 --- a/scripts/manifest_fixer_test.py +++ b/scripts/manifest_fixer_test.py @@ -226,6 +226,47 @@ class RaiseMinSdkVersionTest(unittest.TestCase): self.assertEqual(output, expected) +class AddLoggingParentTest(unittest.TestCase): + """Unit tests for add_logging_parent function.""" + + def add_logging_parent_test(self, input_manifest, logging_parent=None): + doc = minidom.parseString(input_manifest) + if logging_parent: + manifest_fixer.add_logging_parent(doc, logging_parent) + output = StringIO.StringIO() + manifest_fixer.write_xml(output, doc) + return output.getvalue() + + manifest_tmpl = ( + '<?xml version="1.0" encoding="utf-8"?>\n' + '<manifest xmlns:android="http://schemas.android.com/apk/res/android">\n' + '%s' + '</manifest>\n') + + def uses_logging_parent(self, logging_parent=None): + attrs = '' + if logging_parent: + meta_text = ('<meta-data android:name="android.content.pm.LOGGING_PARENT" ' + 'android:value="%s"/>\n') % (logging_parent) + attrs += ' <application>\n %s </application>\n' % (meta_text) + + return attrs + + def test_no_logging_parent(self): + """Tests manifest_fixer with no logging_parent.""" + manifest_input = self.manifest_tmpl % '' + expected = self.manifest_tmpl % self.uses_logging_parent() + output = self.add_logging_parent_test(manifest_input) + self.assertEqual(output, expected) + + def test_logging_parent(self): + """Tests manifest_fixer with no logging_parent.""" + manifest_input = self.manifest_tmpl % '' + expected = self.manifest_tmpl % self.uses_logging_parent('FOO') + output = self.add_logging_parent_test(manifest_input, 'FOO') + self.assertEqual(output, expected) + + class AddUsesLibrariesTest(unittest.TestCase): """Unit tests for add_uses_libraries function.""" diff --git a/sdk/cc_sdk_test.go b/sdk/cc_sdk_test.go index 9a75610f6..8c32d8cad 100644 --- a/sdk/cc_sdk_test.go +++ b/sdk/cc_sdk_test.go @@ -337,6 +337,68 @@ arm64/include/Arm64Test.h -> arm64/include/arm64/include/Arm64Test.h ) } +func TestSnapshotWithCcBinary(t *testing.T) { + result := testSdkWithCc(t, ` + module_exports { + name: "mymodule_exports", + native_binaries: ["mynativebinary"], + } + + cc_binary { + name: "mynativebinary", + srcs: [ + "Test.cpp", + ], + compile_multilib: "both", + system_shared_libs: [], + stl: "none", + } + `) + + result.CheckSnapshot("mymodule_exports", "android_common", "", + checkAndroidBpContents(` +// This is auto-generated. DO NOT EDIT. + +cc_prebuilt_binary { + name: "mymodule_exports_mynativebinary@current", + sdk_member_name: "mynativebinary", + compile_multilib: "both", + arch: { + arm64: { + srcs: ["arm64/bin/mynativebinary"], + }, + arm: { + srcs: ["arm/bin/mynativebinary"], + }, + }, +} + +cc_prebuilt_binary { + name: "mynativebinary", + prefer: false, + compile_multilib: "both", + arch: { + arm64: { + srcs: ["arm64/bin/mynativebinary"], + }, + arm: { + srcs: ["arm/bin/mynativebinary"], + }, + }, +} + +module_exports_snapshot { + name: "mymodule_exports@current", + native_binaries: ["mymodule_exports_mynativebinary@current"], +} +`), + checkAllCopyRules(` +.intermediates/mynativebinary/android_arm64_armv8-a/mynativebinary -> arm64/bin/mynativebinary +.intermediates/mynativebinary/android_arm_armv7-a-neon/mynativebinary -> arm/bin/mynativebinary +`), + ) +} + func TestSnapshotWithCcSharedLibrary(t *testing.T) { result := testSdkWithCc(t, ` sdk { diff --git a/tradefed/autogen.go b/tradefed/autogen.go index c35d8b914..a46dce931 100644 --- a/tradefed/autogen.go +++ b/tradefed/autogen.go @@ -44,17 +44,16 @@ var autogenTestConfig = pctx.StaticRule("autogenTestConfig", blueprint.RuleParam CommandDeps: []string{"$template"}, }, "name", "template", "extraConfigs") -func testConfigPath(ctx android.ModuleContext, prop *string, testSuites []string, autoGenConfig *bool) (path android.Path, autogenPath android.WritablePath) { +func testConfigPath(ctx android.ModuleContext, prop *string, testSuites []string, autoGenConfig *bool, testConfigTemplateProp *string) (path android.Path, autogenPath android.WritablePath) { p := getTestConfig(ctx, prop) if !Bool(autoGenConfig) && p != nil { return p, nil - } else if !android.InList("cts", testSuites) && BoolDefault(autoGenConfig, true) { + } else if BoolDefault(autoGenConfig, true) && (!android.InList("cts", testSuites) || testConfigTemplateProp != nil) { outputFile := android.PathForModuleOut(ctx, ctx.ModuleName()+".config") return nil, outputFile } else { // CTS modules can be used for test data, so test config files must be - // explicitly created using AndroidTest.xml - // TODO(b/112602712): remove the path check + // explicitly created using AndroidTest.xml or test_config_template. return nil, nil } } @@ -130,7 +129,7 @@ func autogenTemplateWithName(ctx android.ModuleContext, name string, output andr func AutoGenNativeTestConfig(ctx android.ModuleContext, testConfigProp *string, testConfigTemplateProp *string, testSuites []string, config []Config, autoGenConfig *bool) android.Path { - path, autogenPath := testConfigPath(ctx, testConfigProp, testSuites, autoGenConfig) + path, autogenPath := testConfigPath(ctx, testConfigProp, testSuites, autoGenConfig, testConfigTemplateProp) if autogenPath != nil { templatePath := getTestConfigTemplate(ctx, testConfigTemplateProp) if templatePath.Valid() { @@ -149,7 +148,7 @@ func AutoGenNativeTestConfig(ctx android.ModuleContext, testConfigProp *string, func AutoGenNativeBenchmarkTestConfig(ctx android.ModuleContext, testConfigProp *string, testConfigTemplateProp *string, testSuites []string, configs []Config, autoGenConfig *bool) android.Path { - path, autogenPath := testConfigPath(ctx, testConfigProp, testSuites, autoGenConfig) + path, autogenPath := testConfigPath(ctx, testConfigProp, testSuites, autoGenConfig, testConfigTemplateProp) if autogenPath != nil { templatePath := getTestConfigTemplate(ctx, testConfigTemplateProp) if templatePath.Valid() { @@ -164,7 +163,7 @@ func AutoGenNativeBenchmarkTestConfig(ctx android.ModuleContext, testConfigProp func AutoGenJavaTestConfig(ctx android.ModuleContext, testConfigProp *string, testConfigTemplateProp *string, testSuites []string, autoGenConfig *bool) android.Path { - path, autogenPath := testConfigPath(ctx, testConfigProp, testSuites, autoGenConfig) + path, autogenPath := testConfigPath(ctx, testConfigProp, testSuites, autoGenConfig, testConfigTemplateProp) if autogenPath != nil { templatePath := getTestConfigTemplate(ctx, testConfigTemplateProp) if templatePath.Valid() { @@ -184,7 +183,7 @@ func AutoGenJavaTestConfig(ctx android.ModuleContext, testConfigProp *string, te func AutoGenPythonBinaryHostTestConfig(ctx android.ModuleContext, testConfigProp *string, testConfigTemplateProp *string, testSuites []string, autoGenConfig *bool) android.Path { - path, autogenPath := testConfigPath(ctx, testConfigProp, testSuites, autoGenConfig) + path, autogenPath := testConfigPath(ctx, testConfigProp, testSuites, autoGenConfig, testConfigTemplateProp) if autogenPath != nil { templatePath := getTestConfigTemplate(ctx, testConfigTemplateProp) if templatePath.Valid() { @@ -199,7 +198,7 @@ func AutoGenPythonBinaryHostTestConfig(ctx android.ModuleContext, testConfigProp func AutoGenRustTestConfig(ctx android.ModuleContext, name string, testConfigProp *string, testConfigTemplateProp *string, testSuites []string, autoGenConfig *bool) android.Path { - path, autogenPath := testConfigPath(ctx, testConfigProp, testSuites, autoGenConfig) + path, autogenPath := testConfigPath(ctx, testConfigProp, testSuites, autoGenConfig, testConfigTemplateProp) if autogenPath != nil { templatePathString := "${RustHostTestConfigTemplate}" if ctx.Device() { @@ -226,7 +225,7 @@ var autogenInstrumentationTest = pctx.StaticRule("autogenInstrumentationTest", b func AutoGenInstrumentationTestConfig(ctx android.ModuleContext, testConfigProp *string, testConfigTemplateProp *string, manifest android.Path, testSuites []string, autoGenConfig *bool) android.Path { - path, autogenPath := testConfigPath(ctx, testConfigProp, testSuites, autoGenConfig) + path, autogenPath := testConfigPath(ctx, testConfigProp, testSuites, autoGenConfig, testConfigTemplateProp) if autogenPath != nil { template := "${InstrumentationTestConfigTemplate}" moduleTemplate := getTestConfigTemplate(ctx, testConfigTemplateProp) diff --git a/ui/build/cleanbuild.go b/ui/build/cleanbuild.go index 1c4f5746f..36d4f0491 100644 --- a/ui/build/cleanbuild.go +++ b/ui/build/cleanbuild.go @@ -237,6 +237,7 @@ func cleanOldFiles(ctx Context, basePath, file string) { if fi.IsDir() { if err := os.Remove(old); err == nil { ctx.Println("Removed directory that is no longer installed: ", old) + cleanEmptyDirs(ctx, filepath.Dir(old)) } else { ctx.Println("Failed to remove directory that is no longer installed (%q): %v", old, err) ctx.Println("It's recommended to run `m installclean`") @@ -244,6 +245,7 @@ func cleanOldFiles(ctx Context, basePath, file string) { } else { if err := os.Remove(old); err == nil { ctx.Println("Removed file that is no longer installed: ", old) + cleanEmptyDirs(ctx, filepath.Dir(old)) } else if !os.IsNotExist(err) { ctx.Fatalf("Failed to remove file that is no longer installed (%q): %v", old, err) } @@ -254,3 +256,16 @@ func cleanOldFiles(ctx Context, basePath, file string) { // Use the new list as the base for the next build os.Rename(file, oldFile) } + +func cleanEmptyDirs(ctx Context, dir string) { + files, err := ioutil.ReadDir(dir) + if err != nil || len(files) > 0 { + return + } + if err := os.Remove(dir); err == nil { + ctx.Println("Removed directory that is no longer installed: ", dir) + } else { + ctx.Fatalf("Failed to remove directory that is no longer installed (%q): %v", dir, err) + } + cleanEmptyDirs(ctx, filepath.Dir(dir)) +} |