diff options
117 files changed, 5959 insertions, 2748 deletions
diff --git a/android/apex.go b/android/apex.go index 60da45bf9..4b744364a 100644 --- a/android/apex.go +++ b/android/apex.go @@ -36,11 +36,16 @@ var ( // Accessible via `ctx.Provider(android.ApexInfoProvider).(android.ApexInfo)` type ApexInfo struct { // Name of the apex variation that this module (i.e. the apex variant of the module) is - // mutated into, or "" for a platform (i.e. non-APEX) variant. Note that a module can be - // included in multiple APEXes, in which case, the module is mutated into one or more - // variants, each of which is for an APEX. The variants then can later be deduped if they - // don't need to be compiled differently. This is an optimization done in - // mergeApexVariations. + // mutated into, or "" for a platform (i.e. non-APEX) variant. Note that this name and the + // Soong module name of the APEX can be different. That happens when there is + // `override_apex` that overrides `apex`. In that case, both Soong modules have the same + // apex variation name which usually is `com.android.foo`. This name is also the `name` + // in the path `/apex/<name>` where this apex is activated on at runtime. + // + // Also note that a module can be included in multiple APEXes, in which case, the module is + // mutated into one or more variants, each of which is for an APEX. The variants then can + // later be deduped if they don't need to be compiled differently. This is an optimization + // done in mergeApexVariations. ApexVariationName string // ApiLevel that this module has to support at minimum. @@ -52,11 +57,19 @@ type ApexInfo struct { // The list of SDK modules that the containing apexBundle depends on. RequiredSdks SdkRefs - // List of apexBundles that this apex variant of the module is associated with. Initially, - // the size of this list is one because one apex variant is associated with one apexBundle. - // When multiple apex variants are merged in mergeApexVariations, ApexInfo struct of the - // merged variant holds the list of apexBundles that are merged together. - InApexes []string + // List of Apex variant names that this module is associated with. This initially is the + // same as the `ApexVariationName` field. Then when multiple apex variants are merged in + // mergeApexVariations, ApexInfo struct of the merged variant holds the list of apexBundles + // that are merged together. + InApexVariants []string + + // List of APEX Soong module names that this module is part of. Note that the list includes + // different variations of the same APEX. For example, if module `foo` is included in the + // apex `com.android.foo`, and also if there is an override_apex module + // `com.mycompany.android.foo` overriding `com.android.foo`, then this list contains both + // `com.android.foo` and `com.mycompany.android.foo`. If the APEX Soong module is a + // prebuilt, the name here doesn't have the `prebuilt_` prefix. + InApexModules []string // Pointers to the ApexContents struct each of which is for apexBundle modules that this // module is part of. The ApexContents gives information about which modules the apexBundle @@ -93,23 +106,33 @@ func (i ApexInfo) IsForPlatform() bool { return i.ApexVariationName == "" } -// InApex tells whether this apex variant of the module is part of the given apexBundle or not. -func (i ApexInfo) InApex(apex string) bool { - for _, a := range i.InApexes { - if a == apex { +// InApexVariant tells whether this apex variant of the module is part of the given apexVariant or +// not. +func (i ApexInfo) InApexVariant(apexVariant string) bool { + for _, a := range i.InApexVariants { + if a == apexVariant { return true } } return false } -// InApexByBaseName tells whether this apex variant of the module is part of the given APEX or not, -// where the APEX is specified by its canonical base name, i.e. typically beginning with +// InApexByBaseName tells whether this apex variant of the module is part of the given apexVariant +// or not, where the APEX is specified by its canonical base name, i.e. typically beginning with // "com.android.". In particular this function doesn't differentiate between source and prebuilt // APEXes, where the latter may have "prebuilt_" prefixes. -func (i ApexInfo) InApexByBaseName(apex string) bool { - for _, a := range i.InApexes { - if RemoveOptionalPrebuiltPrefix(a) == apex { +func (i ApexInfo) InApexVariantByBaseName(apexVariant string) bool { + for _, a := range i.InApexVariants { + if RemoveOptionalPrebuiltPrefix(a) == apexVariant { + return true + } + } + return false +} + +func (i ApexInfo) InApexModule(apexModuleName string) bool { + for _, a := range i.InApexModules { + if a == apexModuleName { return true } } @@ -203,6 +226,12 @@ type ApexModule interface { // apex_available property of the module. AvailableFor(what string) bool + // AlwaysRequiresPlatformApexVariant allows the implementing module to determine whether an + // APEX mutator should always be created for it. + // + // Returns false by default. + AlwaysRequiresPlatformApexVariant() bool + // Returns true if this module is not available to platform (i.e. apex_available property // doesn't have "//apex_available:platform"), or shouldn't be available to platform, which // is the case when this module depends on other module that isn't available to platform. @@ -339,8 +368,21 @@ func (m *ApexModuleBase) ApexAvailable() []string { func (m *ApexModuleBase) BuildForApex(apex ApexInfo) { m.apexInfosLock.Lock() defer m.apexInfosLock.Unlock() - for _, v := range m.apexInfos { + for i, v := range m.apexInfos { if v.ApexVariationName == apex.ApexVariationName { + if len(apex.InApexModules) != 1 { + panic(fmt.Errorf("Newly created apexInfo must be for a single APEX")) + } + // Even when the ApexVariantNames are the same, the given ApexInfo might + // actually be for different APEX. This can happen when an APEX is + // overridden via override_apex. For example, there can be two apexes + // `com.android.foo` (from the `apex` module type) and + // `com.mycompany.android.foo` (from the `override_apex` module type), both + // of which has the same ApexVariantName `com.android.foo`. Add the apex + // name to the list so that it's not lost. + if !InList(apex.InApexModules[0], v.InApexModules) { + m.apexInfos[i].InApexModules = append(m.apexInfos[i].InApexModules, apex.InApexModules[0]) + } return } } @@ -424,6 +466,11 @@ func (m *ApexModuleBase) AvailableFor(what string) bool { } // Implements ApexModule +func (m *ApexModuleBase) AlwaysRequiresPlatformApexVariant() bool { + return false +} + +// Implements ApexModule func (m *ApexModuleBase) NotAvailableForPlatform() bool { return m.ApexProperties.NotAvailableForPlatform } @@ -485,21 +532,23 @@ func mergeApexVariations(ctx PathContext, apexInfos []ApexInfo) (merged []ApexIn // Merge the ApexInfo together. If a compatible ApexInfo exists then merge the information from // this one into it, otherwise create a new merged ApexInfo from this one and save it away so // other ApexInfo instances can be merged into it. - apexName := apexInfo.ApexVariationName + variantName := apexInfo.ApexVariationName mergedName := apexInfo.mergedName(ctx) if index, exists := seen[mergedName]; exists { // Variants having the same mergedName are deduped - merged[index].InApexes = append(merged[index].InApexes, apexName) + merged[index].InApexVariants = append(merged[index].InApexVariants, variantName) + merged[index].InApexModules = append(merged[index].InApexModules, apexInfo.InApexModules...) merged[index].ApexContents = append(merged[index].ApexContents, apexInfo.ApexContents...) merged[index].Updatable = merged[index].Updatable || apexInfo.Updatable } else { seen[mergedName] = len(merged) apexInfo.ApexVariationName = mergedName - apexInfo.InApexes = CopyOf(apexInfo.InApexes) + apexInfo.InApexVariants = CopyOf(apexInfo.InApexVariants) + apexInfo.InApexModules = CopyOf(apexInfo.InApexModules) apexInfo.ApexContents = append([]*ApexContents(nil), apexInfo.ApexContents...) merged = append(merged, apexInfo) } - aliases = append(aliases, [2]string{apexName, mergedName}) + aliases = append(aliases, [2]string{variantName, mergedName}) } return merged, aliases } @@ -572,15 +621,15 @@ func CreateApexVariations(mctx BottomUpMutatorContext, module ApexModule) []Modu // in the same APEX have unique APEX variations so that the module can link against the right // variant. func UpdateUniqueApexVariationsForDeps(mctx BottomUpMutatorContext, am ApexModule) { - // anyInSameApex returns true if the two ApexInfo lists contain any values in an InApexes - // list in common. It is used instead of DepIsInSameApex because it needs to determine if - // the dep is in the same APEX due to being directly included, not only if it is included - // _because_ it is a dependency. + // anyInSameApex returns true if the two ApexInfo lists contain any values in an + // InApexVariants list in common. It is used instead of DepIsInSameApex because it needs to + // determine if the dep is in the same APEX due to being directly included, not only if it + // is included _because_ it is a dependency. anyInSameApex := func(a, b []ApexInfo) bool { collectApexes := func(infos []ApexInfo) []string { var ret []string for _, info := range infos { - ret = append(ret, info.InApexes...) + ret = append(ret, info.InApexVariants...) } return ret } diff --git a/android/apex_test.go b/android/apex_test.go index 109b1c8b2..e1123692d 100644 --- a/android/apex_test.go +++ b/android/apex_test.go @@ -33,10 +33,10 @@ func Test_mergeApexVariations(t *testing.T) { { name: "single", in: []ApexInfo{ - {"foo", FutureApiLevel, false, nil, []string{"foo"}, nil, NotForPrebuiltApex}, + {"foo", FutureApiLevel, false, nil, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex}, }, wantMerged: []ApexInfo{ - {"apex10000", FutureApiLevel, false, nil, []string{"foo"}, nil, NotForPrebuiltApex}, + {"apex10000", FutureApiLevel, false, nil, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex}, }, wantAliases: [][2]string{ {"foo", "apex10000"}, @@ -45,11 +45,11 @@ func Test_mergeApexVariations(t *testing.T) { { name: "merge", in: []ApexInfo{ - {"foo", FutureApiLevel, false, SdkRefs{{"baz", "1"}}, []string{"foo"}, nil, NotForPrebuiltApex}, - {"bar", FutureApiLevel, false, SdkRefs{{"baz", "1"}}, []string{"bar"}, nil, NotForPrebuiltApex}, + {"foo", FutureApiLevel, false, SdkRefs{{"baz", "1"}}, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex}, + {"bar", FutureApiLevel, false, SdkRefs{{"baz", "1"}}, []string{"bar"}, []string{"bar"}, nil, NotForPrebuiltApex}, }, wantMerged: []ApexInfo{ - {"apex10000_baz_1", FutureApiLevel, false, SdkRefs{{"baz", "1"}}, []string{"bar", "foo"}, nil, false}}, + {"apex10000_baz_1", FutureApiLevel, false, SdkRefs{{"baz", "1"}}, []string{"bar", "foo"}, []string{"bar", "foo"}, nil, false}}, wantAliases: [][2]string{ {"bar", "apex10000_baz_1"}, {"foo", "apex10000_baz_1"}, @@ -58,12 +58,12 @@ func Test_mergeApexVariations(t *testing.T) { { name: "don't merge version", in: []ApexInfo{ - {"foo", FutureApiLevel, false, nil, []string{"foo"}, nil, NotForPrebuiltApex}, - {"bar", uncheckedFinalApiLevel(30), false, nil, []string{"bar"}, nil, NotForPrebuiltApex}, + {"foo", FutureApiLevel, false, nil, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex}, + {"bar", uncheckedFinalApiLevel(30), false, nil, []string{"bar"}, []string{"bar"}, nil, NotForPrebuiltApex}, }, wantMerged: []ApexInfo{ - {"apex30", uncheckedFinalApiLevel(30), false, nil, []string{"bar"}, nil, NotForPrebuiltApex}, - {"apex10000", FutureApiLevel, false, nil, []string{"foo"}, nil, NotForPrebuiltApex}, + {"apex30", uncheckedFinalApiLevel(30), false, nil, []string{"bar"}, []string{"bar"}, nil, NotForPrebuiltApex}, + {"apex10000", FutureApiLevel, false, nil, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex}, }, wantAliases: [][2]string{ {"bar", "apex30"}, @@ -73,11 +73,11 @@ func Test_mergeApexVariations(t *testing.T) { { name: "merge updatable", in: []ApexInfo{ - {"foo", FutureApiLevel, false, nil, []string{"foo"}, nil, NotForPrebuiltApex}, - {"bar", FutureApiLevel, true, nil, []string{"bar"}, nil, NotForPrebuiltApex}, + {"foo", FutureApiLevel, false, nil, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex}, + {"bar", FutureApiLevel, true, nil, []string{"bar"}, []string{"bar"}, nil, NotForPrebuiltApex}, }, wantMerged: []ApexInfo{ - {"apex10000", FutureApiLevel, true, nil, []string{"bar", "foo"}, nil, NotForPrebuiltApex}, + {"apex10000", FutureApiLevel, true, nil, []string{"bar", "foo"}, []string{"bar", "foo"}, nil, NotForPrebuiltApex}, }, wantAliases: [][2]string{ {"bar", "apex10000"}, @@ -87,12 +87,12 @@ func Test_mergeApexVariations(t *testing.T) { { name: "don't merge sdks", in: []ApexInfo{ - {"foo", FutureApiLevel, false, SdkRefs{{"baz", "1"}}, []string{"foo"}, nil, NotForPrebuiltApex}, - {"bar", FutureApiLevel, false, SdkRefs{{"baz", "2"}}, []string{"bar"}, nil, NotForPrebuiltApex}, + {"foo", FutureApiLevel, false, SdkRefs{{"baz", "1"}}, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex}, + {"bar", FutureApiLevel, false, SdkRefs{{"baz", "2"}}, []string{"bar"}, []string{"bar"}, nil, NotForPrebuiltApex}, }, wantMerged: []ApexInfo{ - {"apex10000_baz_2", FutureApiLevel, false, SdkRefs{{"baz", "2"}}, []string{"bar"}, nil, NotForPrebuiltApex}, - {"apex10000_baz_1", FutureApiLevel, false, SdkRefs{{"baz", "1"}}, []string{"foo"}, nil, NotForPrebuiltApex}, + {"apex10000_baz_2", FutureApiLevel, false, SdkRefs{{"baz", "2"}}, []string{"bar"}, []string{"bar"}, nil, NotForPrebuiltApex}, + {"apex10000_baz_1", FutureApiLevel, false, SdkRefs{{"baz", "1"}}, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex}, }, wantAliases: [][2]string{ {"bar", "apex10000_baz_2"}, @@ -102,15 +102,15 @@ func Test_mergeApexVariations(t *testing.T) { { name: "don't merge when for prebuilt_apex", in: []ApexInfo{ - {"foo", FutureApiLevel, false, nil, []string{"foo"}, nil, NotForPrebuiltApex}, - {"bar", FutureApiLevel, true, nil, []string{"bar"}, nil, NotForPrebuiltApex}, + {"foo", FutureApiLevel, false, nil, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex}, + {"bar", FutureApiLevel, true, nil, []string{"bar"}, []string{"bar"}, nil, NotForPrebuiltApex}, // This one should not be merged in with the others because it is for // a prebuilt_apex. - {"baz", FutureApiLevel, true, nil, []string{"baz"}, nil, ForPrebuiltApex}, + {"baz", FutureApiLevel, true, nil, []string{"baz"}, []string{"baz"}, nil, ForPrebuiltApex}, }, wantMerged: []ApexInfo{ - {"apex10000", FutureApiLevel, true, nil, []string{"bar", "foo"}, nil, NotForPrebuiltApex}, - {"baz", FutureApiLevel, true, nil, []string{"baz"}, nil, ForPrebuiltApex}, + {"apex10000", FutureApiLevel, true, nil, []string{"bar", "foo"}, []string{"bar", "foo"}, nil, NotForPrebuiltApex}, + {"baz", FutureApiLevel, true, nil, []string{"baz"}, []string{"baz"}, nil, ForPrebuiltApex}, }, wantAliases: [][2]string{ {"bar", "apex10000"}, diff --git a/android/api_levels.go b/android/api_levels.go index 9bc7e837b..84ab27c4c 100644 --- a/android/api_levels.go +++ b/android/api_levels.go @@ -158,6 +158,21 @@ var NoneApiLevel = ApiLevel{ // The first version that introduced 64-bit ABIs. var FirstLp64Version = uncheckedFinalApiLevel(21) +// Android has had various kinds of packed relocations over the years +// (http://b/187907243). +// +// API level 30 is where the now-standard SHT_RELR is available. +var FirstShtRelrVersion = uncheckedFinalApiLevel(30) + +// API level 28 introduced SHT_RELR when it was still Android-only, and used an +// Android-specific relocation. +var FirstAndroidRelrVersion = uncheckedFinalApiLevel(28) + +// API level 23 was when we first had the Chrome relocation packer, which is +// obsolete and has been removed, but lld can now generate compatible packed +// relocations itself. +var FirstPackedRelocationsVersion = uncheckedFinalApiLevel(23) + // The first API level that does not require NDK code to link // libandroid_support. var FirstNonLibAndroidSupportVersion = uncheckedFinalApiLevel(21) diff --git a/android/arch.go b/android/arch.go index c1b2c33ca..10c827ba2 100644 --- a/android/arch.go +++ b/android/arch.go @@ -1025,7 +1025,7 @@ func maybeBlueprintEmbed(src reflect.Value) reflect.Value { } // Merges the property struct in srcValue into dst. -func mergePropertyStruct(ctx BaseMutatorContext, dst interface{}, srcValue reflect.Value) { +func mergePropertyStruct(ctx ArchVariantContext, dst interface{}, srcValue reflect.Value) { src := maybeBlueprintEmbed(srcValue).Interface() // order checks the `android:"variant_prepend"` tag to handle properties where the @@ -1054,25 +1054,29 @@ func mergePropertyStruct(ctx BaseMutatorContext, dst interface{}, srcValue refle // Returns the immediate child of the input property struct that corresponds to // the sub-property "field". -func getChildPropertyStruct(ctx BaseMutatorContext, - src reflect.Value, field, userFriendlyField string) reflect.Value { +func getChildPropertyStruct(ctx ArchVariantContext, + src reflect.Value, field, userFriendlyField string) (reflect.Value, bool) { // Step into non-nil pointers to structs in the src value. if src.Kind() == reflect.Ptr { if src.IsNil() { - return src + return reflect.Value{}, false } src = src.Elem() } // Find the requested field in the src struct. - src = src.FieldByName(proptools.FieldNameForProperty(field)) - if !src.IsValid() { + child := src.FieldByName(proptools.FieldNameForProperty(field)) + if !child.IsValid() { ctx.ModuleErrorf("field %q does not exist", userFriendlyField) - return src + return reflect.Value{}, false + } + + if child.IsZero() { + return reflect.Value{}, false } - return src + return child, true } // Squash the appropriate OS-specific property structs into the matching top level property structs @@ -1099,8 +1103,9 @@ func (m *ModuleBase) setOSProperties(ctx BottomUpMutatorContext) { if os.Class == Host { field := "Host" prefix := "target.host" - hostProperties := getChildPropertyStruct(ctx, targetProp, field, prefix) - mergePropertyStruct(ctx, genProps, hostProperties) + if hostProperties, ok := getChildPropertyStruct(ctx, targetProp, field, prefix); ok { + mergePropertyStruct(ctx, genProps, hostProperties) + } } // Handle target OS generalities of the form: @@ -1112,15 +1117,17 @@ func (m *ModuleBase) setOSProperties(ctx BottomUpMutatorContext) { if os.Linux() { field := "Linux" prefix := "target.linux" - linuxProperties := getChildPropertyStruct(ctx, targetProp, field, prefix) - mergePropertyStruct(ctx, genProps, linuxProperties) + if linuxProperties, ok := getChildPropertyStruct(ctx, targetProp, field, prefix); ok { + mergePropertyStruct(ctx, genProps, linuxProperties) + } } if os.Bionic() { field := "Bionic" prefix := "target.bionic" - bionicProperties := getChildPropertyStruct(ctx, targetProp, field, prefix) - mergePropertyStruct(ctx, genProps, bionicProperties) + if bionicProperties, ok := getChildPropertyStruct(ctx, targetProp, field, prefix); ok { + mergePropertyStruct(ctx, genProps, bionicProperties) + } } // Handle target OS properties in the form: @@ -1137,14 +1144,16 @@ func (m *ModuleBase) setOSProperties(ctx BottomUpMutatorContext) { // }, field := os.Field prefix := "target." + os.Name - osProperties := getChildPropertyStruct(ctx, targetProp, field, prefix) - mergePropertyStruct(ctx, genProps, osProperties) + if osProperties, ok := getChildPropertyStruct(ctx, targetProp, field, prefix); ok { + mergePropertyStruct(ctx, genProps, osProperties) + } if os.Class == Host && os != Windows { field := "Not_windows" prefix := "target.not_windows" - notWindowsProperties := getChildPropertyStruct(ctx, targetProp, field, prefix) - mergePropertyStruct(ctx, genProps, notWindowsProperties) + if notWindowsProperties, ok := getChildPropertyStruct(ctx, targetProp, field, prefix); ok { + mergePropertyStruct(ctx, genProps, notWindowsProperties) + } } // Handle 64-bit device properties in the form: @@ -1164,13 +1173,15 @@ func (m *ModuleBase) setOSProperties(ctx BottomUpMutatorContext) { if ctx.Config().Android64() { field := "Android64" prefix := "target.android64" - android64Properties := getChildPropertyStruct(ctx, targetProp, field, prefix) - mergePropertyStruct(ctx, genProps, android64Properties) + if android64Properties, ok := getChildPropertyStruct(ctx, targetProp, field, prefix); ok { + mergePropertyStruct(ctx, genProps, android64Properties) + } } else { field := "Android32" prefix := "target.android32" - android32Properties := getChildPropertyStruct(ctx, targetProp, field, prefix) - mergePropertyStruct(ctx, genProps, android32Properties) + if android32Properties, ok := getChildPropertyStruct(ctx, targetProp, field, prefix); ok { + mergePropertyStruct(ctx, genProps, android32Properties) + } } } } @@ -1186,12 +1197,11 @@ func (m *ModuleBase) setOSProperties(ctx BottomUpMutatorContext) { // }, // This struct will also contain sub-structs containing to the architecture/CPU // variants and features that themselves contain properties specific to those. -func getArchTypeStruct(ctx BaseMutatorContext, archProperties interface{}, archType ArchType) reflect.Value { +func getArchTypeStruct(ctx ArchVariantContext, archProperties interface{}, archType ArchType) (reflect.Value, bool) { archPropValues := reflect.ValueOf(archProperties).Elem() archProp := archPropValues.FieldByName("Arch").Elem() prefix := "arch." + archType.Name - archStruct := getChildPropertyStruct(ctx, archProp, archType.Name, prefix) - return archStruct + return getChildPropertyStruct(ctx, archProp, archType.Name, prefix) } // Returns the struct containing the properties specific to a given multilib @@ -1201,11 +1211,10 @@ func getArchTypeStruct(ctx BaseMutatorContext, archProperties interface{}, archT // key: value, // }, // }, -func getMultilibStruct(ctx BaseMutatorContext, archProperties interface{}, archType ArchType) reflect.Value { +func getMultilibStruct(ctx ArchVariantContext, archProperties interface{}, archType ArchType) (reflect.Value, bool) { archPropValues := reflect.ValueOf(archProperties).Elem() multilibProp := archPropValues.FieldByName("Multilib").Elem() - multilibProperties := getChildPropertyStruct(ctx, multilibProp, archType.Multilib, "multilib."+archType.Multilib) - return multilibProperties + return getChildPropertyStruct(ctx, multilibProp, archType.Multilib, "multilib."+archType.Multilib) } // Returns the structs corresponding to the properties specific to the given @@ -1219,57 +1228,63 @@ func getArchProperties(ctx BaseMutatorContext, archProperties interface{}, arch archType := arch.ArchType if arch.ArchType != Common { - archStruct := getArchTypeStruct(ctx, archProperties, arch.ArchType) - result = append(result, archStruct) - - // Handle arch-variant-specific properties in the form: - // arch: { - // arm: { - // variant: { - // key: value, - // }, - // }, - // }, - v := variantReplacer.Replace(arch.ArchVariant) - if v != "" { - prefix := "arch." + archType.Name + "." + v - variantProperties := getChildPropertyStruct(ctx, archStruct, v, prefix) - result = append(result, variantProperties) - } - - // Handle cpu-variant-specific properties in the form: - // arch: { - // arm: { - // variant: { - // key: value, - // }, - // }, - // }, - if arch.CpuVariant != arch.ArchVariant { - c := variantReplacer.Replace(arch.CpuVariant) - if c != "" { - prefix := "arch." + archType.Name + "." + c - cpuVariantProperties := getChildPropertyStruct(ctx, archStruct, c, prefix) - result = append(result, cpuVariantProperties) + archStruct, ok := getArchTypeStruct(ctx, archProperties, arch.ArchType) + if ok { + result = append(result, archStruct) + + // Handle arch-variant-specific properties in the form: + // arch: { + // arm: { + // variant: { + // key: value, + // }, + // }, + // }, + v := variantReplacer.Replace(arch.ArchVariant) + if v != "" { + prefix := "arch." + archType.Name + "." + v + if variantProperties, ok := getChildPropertyStruct(ctx, archStruct, v, prefix); ok { + result = append(result, variantProperties) + } } - } - // Handle arch-feature-specific properties in the form: - // arch: { - // arm: { - // feature: { - // key: value, - // }, - // }, - // }, - for _, feature := range arch.ArchFeatures { - prefix := "arch." + archType.Name + "." + feature - featureProperties := getChildPropertyStruct(ctx, archStruct, feature, prefix) - result = append(result, featureProperties) + // Handle cpu-variant-specific properties in the form: + // arch: { + // arm: { + // variant: { + // key: value, + // }, + // }, + // }, + if arch.CpuVariant != arch.ArchVariant { + c := variantReplacer.Replace(arch.CpuVariant) + if c != "" { + prefix := "arch." + archType.Name + "." + c + if cpuVariantProperties, ok := getChildPropertyStruct(ctx, archStruct, c, prefix); ok { + result = append(result, cpuVariantProperties) + } + } + } + + // Handle arch-feature-specific properties in the form: + // arch: { + // arm: { + // feature: { + // key: value, + // }, + // }, + // }, + for _, feature := range arch.ArchFeatures { + prefix := "arch." + archType.Name + "." + feature + if featureProperties, ok := getChildPropertyStruct(ctx, archStruct, feature, prefix); ok { + result = append(result, featureProperties) + } + } } - multilibProperties := getMultilibStruct(ctx, archProperties, archType) - result = append(result, multilibProperties) + if multilibProperties, ok := getMultilibStruct(ctx, archProperties, archType); ok { + result = append(result, multilibProperties) + } // Handle combined OS-feature and arch specific properties in the form: // target: { @@ -1280,15 +1295,17 @@ func getArchProperties(ctx BaseMutatorContext, archProperties interface{}, arch if os.Linux() { field := "Linux_" + arch.ArchType.Name userFriendlyField := "target.linux_" + arch.ArchType.Name - linuxProperties := getChildPropertyStruct(ctx, targetProp, field, userFriendlyField) - result = append(result, linuxProperties) + if linuxProperties, ok := getChildPropertyStruct(ctx, targetProp, field, userFriendlyField); ok { + result = append(result, linuxProperties) + } } if os.Bionic() { field := "Bionic_" + archType.Name userFriendlyField := "target.bionic_" + archType.Name - bionicProperties := getChildPropertyStruct(ctx, targetProp, field, userFriendlyField) - result = append(result, bionicProperties) + if bionicProperties, ok := getChildPropertyStruct(ctx, targetProp, field, userFriendlyField); ok { + result = append(result, bionicProperties) + } } // Handle combined OS and arch specific properties in the form: @@ -1308,8 +1325,9 @@ func getArchProperties(ctx BaseMutatorContext, archProperties interface{}, arch // }, field := os.Field + "_" + archType.Name userFriendlyField := "target." + os.Name + "_" + archType.Name - osArchProperties := getChildPropertyStruct(ctx, targetProp, field, userFriendlyField) - result = append(result, osArchProperties) + if osArchProperties, ok := getChildPropertyStruct(ctx, targetProp, field, userFriendlyField); ok { + result = append(result, osArchProperties) + } } // Handle arm on x86 properties in the form: @@ -1326,21 +1344,24 @@ func getArchProperties(ctx BaseMutatorContext, archProperties interface{}, arch hasArmAndroidArch(ctx.Config().Targets[Android])) { field := "Arm_on_x86" userFriendlyField := "target.arm_on_x86" - armOnX86Properties := getChildPropertyStruct(ctx, targetProp, field, userFriendlyField) - result = append(result, armOnX86Properties) + if armOnX86Properties, ok := getChildPropertyStruct(ctx, targetProp, field, userFriendlyField); ok { + result = append(result, armOnX86Properties) + } } if arch.ArchType == X86_64 && (hasArmAbi(arch) || hasArmAndroidArch(ctx.Config().Targets[Android])) { field := "Arm_on_x86_64" userFriendlyField := "target.arm_on_x86_64" - armOnX8664Properties := getChildPropertyStruct(ctx, targetProp, field, userFriendlyField) - result = append(result, armOnX8664Properties) + if armOnX8664Properties, ok := getChildPropertyStruct(ctx, targetProp, field, userFriendlyField); ok { + result = append(result, armOnX8664Properties) + } } if os == Android && nativeBridgeEnabled { userFriendlyField := "Native_bridge" prefix := "target.native_bridge" - nativeBridgeProperties := getChildPropertyStruct(ctx, targetProp, userFriendlyField, prefix) - result = append(result, nativeBridgeProperties) + if nativeBridgeProperties, ok := getChildPropertyStruct(ctx, targetProp, userFriendlyField, prefix); ok { + result = append(result, nativeBridgeProperties) + } } } @@ -1851,6 +1872,12 @@ func (m *ModuleBase) getMultilibPropertySet(propertySet interface{}, archType Ar return reflect.New(reflect.ValueOf(propertySet).Elem().Type()).Interface() } +// ArchVariantContext defines the limited context necessary to retrieve arch_variant properties. +type ArchVariantContext interface { + ModuleErrorf(fmt string, args ...interface{}) + PropertyErrorf(property, fmt string, args ...interface{}) +} + // GetArchProperties returns a map of architectures to the values of the // properties of the 'propertySet' struct that are specific to that architecture. // @@ -1863,7 +1890,9 @@ func (m *ModuleBase) getMultilibPropertySet(propertySet interface{}, archType Ar // For example: `arch: { x86: { Foo: ["bar"] } }, multilib: { lib32: {` Foo: ["baz"] } }` // will result in `Foo: ["bar", "baz"]` being returned for architecture x86, if the given // propertyset contains `Foo []string`. -func (m *ModuleBase) GetArchProperties(ctx BaseMutatorContext, propertySet interface{}) map[ArchType]interface{} { +// +// Implemented in a way very similar to GetTargetProperties(). +func (m *ModuleBase) GetArchProperties(ctx ArchVariantContext, propertySet interface{}) map[ArchType]interface{} { // Return value of the arch types to the prop values for that arch. archToProp := map[ArchType]interface{}{} @@ -1897,9 +1926,14 @@ func (m *ModuleBase) GetArchProperties(ctx BaseMutatorContext, propertySet inter // input one that contains the data specific to that arch. propertyStructs := make([]reflect.Value, 0) for _, archProperty := range archProperties { - archTypeStruct := getArchTypeStruct(ctx, archProperty, arch) - multilibStruct := getMultilibStruct(ctx, archProperty, arch) - propertyStructs = append(propertyStructs, archTypeStruct, multilibStruct) + archTypeStruct, ok := getArchTypeStruct(ctx, archProperty, arch) + if ok { + propertyStructs = append(propertyStructs, archTypeStruct) + } + multilibStruct, ok := getMultilibStruct(ctx, archProperty, arch) + if ok { + propertyStructs = append(propertyStructs, multilibStruct) + } } // Create a new instance of the requested property set @@ -1916,18 +1950,31 @@ func (m *ModuleBase) GetArchProperties(ctx BaseMutatorContext, propertySet inter return archToProp } +// Returns the struct containing the properties specific to the given +// architecture type. These look like this in Blueprint files: +// target: { +// android: { +// key: value, +// }, +// }, +// This struct will also contain sub-structs containing to the architecture/CPU +// variants and features that themselves contain properties specific to those. +func getTargetStruct(ctx ArchVariantContext, archProperties interface{}, os OsType) (reflect.Value, bool) { + archPropValues := reflect.ValueOf(archProperties).Elem() + targetProp := archPropValues.FieldByName("Target").Elem() + return getChildPropertyStruct(ctx, targetProp, os.Field, os.Field) +} + // GetTargetProperties returns a map of OS target (e.g. android, windows) to the -// values of the properties of the 'dst' struct that are specific to that OS -// target. +// values of the properties of the 'propertySet' struct that are specific to +// that OS target. // // For example, passing a struct { Foo bool, Bar string } will return an // interface{} that can be type asserted back into the same struct, containing // the os-specific property value specified by the module if defined. // -// While this looks similar to GetArchProperties, the internal representation of -// the properties have a slightly different layout to warrant a standalone -// lookup function. -func (m *ModuleBase) GetTargetProperties(dst interface{}) map[OsType]interface{} { +// Implemented in a way very similar to GetArchProperties(). +func (m *ModuleBase) GetTargetProperties(ctx ArchVariantContext, propertySet interface{}) map[OsType]interface{} { // Return value of the arch types to the prop values for that arch. osToProp := map[OsType]interface{}{} @@ -1936,69 +1983,48 @@ func (m *ModuleBase) GetTargetProperties(dst interface{}) map[OsType]interface{} return osToProp } - // archProperties has the type of [][]interface{}. Looks complicated, so - // let's explain this step by step. - // - // Loop over the outer index, which determines the property struct that - // contains a matching set of properties in dst that we're interested in. - // For example, BaseCompilerProperties or BaseLinkerProperties. - for i := range m.archProperties { - if m.archProperties[i] == nil { - continue - } - - // Iterate over the supported OS types - for _, os := range osTypeList { - // e.g android, linux_bionic - field := os.Field - - // If it's not nil, loop over the inner index, which determines the arch variant - // of the prop type. In an Android.bp file, this is like looping over: - // - // target: { android: { key: value, ... }, linux_bionic: { key: value, ... } } - for _, archProperties := range m.archProperties[i] { - archPropValues := reflect.ValueOf(archProperties).Elem() - - // This is the archPropRoot struct. Traverse into the Targetnested struct. - src := archPropValues.FieldByName("Target").Elem() - - // Step into non-nil pointers to structs in the src value. - if src.Kind() == reflect.Ptr { - if src.IsNil() { - continue - } - src = src.Elem() - } - - // Find the requested field (e.g. android, linux_bionic) in the src struct. - src = src.FieldByName(field) + dstType := reflect.ValueOf(propertySet).Type() + var archProperties []interface{} - // Validation steps. We want valid non-nil pointers to structs. - if !src.IsValid() || src.IsNil() { - continue - } + // First find the property set in the module that corresponds to the requested + // one. m.archProperties[i] corresponds to m.generalProperties[i]. + for i, generalProp := range m.generalProperties { + srcType := reflect.ValueOf(generalProp).Type() + if srcType == dstType { + archProperties = m.archProperties[i] + break + } + } - if src.Kind() != reflect.Ptr || src.Elem().Kind() != reflect.Struct { - continue - } + if archProperties == nil { + // This module does not have the property set requested + return osToProp + } - // Clone the destination prop, since we want a unique prop struct per arch. - dstClone := reflect.New(reflect.ValueOf(dst).Elem().Type()).Interface() + for _, os := range osTypeList { + if os == CommonOS { + // It looks like this OS value is not used in Blueprint files + continue + } - // Copy the located property struct into the cloned destination property struct. - err := proptools.ExtendMatchingProperties([]interface{}{dstClone}, src.Interface(), nil, proptools.OrderReplace) - if err != nil { - // This is fine, it just means the src struct doesn't match. - continue - } + propertyStructs := make([]reflect.Value, 0) + for _, archProperty := range archProperties { + targetStruct, ok := getTargetStruct(ctx, archProperty, os) + if ok { + propertyStructs = append(propertyStructs, targetStruct) + } + } - // Found the prop for the os, you have. - osToProp[os] = dstClone + // Create a new instance of the requested property set + value := reflect.New(reflect.ValueOf(propertySet).Elem().Type()).Interface() - // Go to the next prop. - break - } + // Merge all the structs together + for _, propertyStruct := range propertyStructs { + mergePropertyStruct(ctx, value, propertyStruct) } + + osToProp[os] = value } + return osToProp } diff --git a/android/bazel.go b/android/bazel.go index 0af4aa0ea..f56c24e04 100644 --- a/android/bazel.go +++ b/android/bazel.go @@ -126,40 +126,21 @@ const ( ) var ( - // Do not write BUILD files for these directories - // NOTE: this is not recursive - bp2buildDoNotWriteBuildFileList = []string{ - // Don't generate these BUILD files - because external BUILD files already exist - "external/boringssl", - "external/brotli", - "external/dagger2", - "external/flatbuffers", - "external/gflags", - "external/google-fruit", - "external/grpc-grpc", - "external/grpc-grpc/test/core/util", - "external/grpc-grpc/test/cpp/common", - "external/grpc-grpc/third_party/address_sorting", - "external/nanopb-c", - "external/nos/host/generic", - "external/nos/host/generic/libnos", - "external/nos/host/generic/libnos/generator", - "external/nos/host/generic/libnos_datagram", - "external/nos/host/generic/libnos_transport", - "external/nos/host/generic/nugget/proto", - "external/perfetto", - "external/protobuf", - "external/rust/cxx", - "external/rust/cxx/demo", - "external/ruy", - "external/tensorflow", - "external/tensorflow/tensorflow/lite", - "external/tensorflow/tensorflow/lite/java", - "external/tensorflow/tensorflow/lite/kernels", - "external/tflite-support", - "external/tinyalsa_new", - "external/wycheproof", - "external/libyuv", + // Keep any existing BUILD files (and do not generate new BUILD files) for these directories + bp2buildKeepExistingBuildFile = map[string]bool{ + // This is actually build/bazel/build.BAZEL symlinked to ./BUILD + ".":/*recrusive = */ false, + + "build/bazel":/* recursive = */ true, + "build/pesto":/* recursive = */ true, + + // external/bazelbuild-rules_android/... is needed by mixed builds, otherwise mixed builds analysis fails + // e.g. ERROR: Analysis of target '@soong_injection//mixed_builds:buildroot' failed + "external/bazelbuild-rules_android":/* recursive = */ true, + + "prebuilts/clang/host/linux-x86":/* recursive = */ false, + "prebuilts/sdk":/* recursive = */ false, + "prebuilts/sdk/tools":/* recursive = */ false, } // Configure modules in these directories to enable bp2build_available: true or false by default. @@ -173,19 +154,12 @@ var ( "external/jemalloc_new": Bp2BuildDefaultTrueRecursively, "external/fmtlib": Bp2BuildDefaultTrueRecursively, "external/arm-optimized-routines": Bp2BuildDefaultTrueRecursively, + "external/scudo": Bp2BuildDefaultTrueRecursively, } // Per-module denylist to always opt modules out of both bp2build and mixed builds. bp2buildModuleDoNotConvertList = []string{ // Things that transitively depend on unconverted libc_* modules. - "libc_nopthread", // http://b/186821550, cc_library_static, depends on //bionic/libc:libc_bionic_ndk (http://b/186822256) - // also depends on //bionic/libc:libc_tzcode (http://b/186822591) - // also depends on //bionic/libc:libstdc++ (http://b/186822597) - "libc_common", // http://b/186821517, cc_library_static, depends on //bionic/libc:libc_nopthread (http://b/186821550) - "libc_common_static", // http://b/186824119, cc_library_static, depends on //bionic/libc:libc_common (http://b/186821517) - "libc_common_shared", // http://b/186824118, cc_library_static, depends on //bionic/libc:libc_common (http://b/186821517) - "libc_nomalloc", // http://b/186825031, cc_library_static, depends on //bionic/libc:libc_common (http://b/186821517) - "libbionic_spawn_benchmark", // http://b/186824595, cc_library_static, depends on //external/google-benchmark (http://b/186822740) // also depends on //system/logging/liblog:liblog (http://b/186822772) @@ -204,12 +178,10 @@ var ( "liblinker_malloc", // http://b/186826466, cc_library_static, depends on //external/zlib:libz (http://b/186823782) // also depends on //system/libziparchive:libziparchive (http://b/186823656) // also depends on //system/logging/liblog:liblog (http://b/186822772) - "libc_jemalloc_wrapper", // http://b/187012490, cc_library_static, depends on //external/jemalloc_new:libjemalloc5 (http://b/186828626) - "libc_ndk", // http://b/187013218, cc_library_static, depends on //bionic/libm:libm (http://b/183064661) - "libc", // http://b/183064430, cc_library, depends on //external/jemalloc_new:libjemalloc5 (http://b/186828626) - "libc_bionic_ndk", // http://b/186822256, cc_library_static, signal.cpp:186:52: error: ISO C++ requires field designators to be specified in declaration order - "libc_malloc_hooks", // http://b/187016307, cc_library, ld.lld: error: undefined symbol: __malloc_hook - "libm", // http://b/183064661, cc_library, math.h:25:16: error: unexpected token in argument list + "libc_ndk", // http://b/187013218, cc_library_static, depends on //bionic/libm:libm (http://b/183064661) + "libc", // http://b/183064430, cc_library, depends on //external/jemalloc_new:libjemalloc5 (http://b/186828626) + "libc_malloc_hooks", // http://b/187016307, cc_library, ld.lld: error: undefined symbol: __malloc_hook + "libm", // http://b/183064661, cc_library, math.h:25:16: error: unexpected token in argument list // http://b/186823769: Needs C++ STL support, includes from unconverted standard libraries in //external/libcxx // c++_static @@ -217,6 +189,7 @@ var ( // libcxx "libBionicBenchmarksUtils", // cc_library_static, fatal error: 'map' file not found, from libcxx "fmtlib", // cc_library_static, fatal error: 'cassert' file not found, from libcxx + "fmtlib_ndk", // cc_library_static, fatal error: 'cassert' file not found "libbase", // http://b/186826479, cc_library, fatal error: 'memory' file not found, from libcxx // http://b/186024507: Includes errors because of the system_shared_libs default value. @@ -228,6 +201,8 @@ var ( "note_memtag_heap_async", // http://b/185127353: cc_library_static, error: feature.h not found "note_memtag_heap_sync", // http://b/185127353: cc_library_static, error: feature.h not found + "gwp_asan_crash_handler", // cc_library, ld.lld: error: undefined symbol: memset + // Tests. Handle later. "libbionic_tests_headers_posix", // http://b/186024507, cc_library_static, sched.h, time.h not found "libjemalloc5_integrationtest", @@ -238,32 +213,31 @@ var ( // Per-module denylist of cc_library modules to only generate the static // variant if their shared variant isn't ready or buildable by Bazel. bp2buildCcLibraryStaticOnlyList = []string{ - "libstdc++", // http://b/186822597, cc_library, ld.lld: error: undefined symbol: __errno + "libstdc++", // http://b/186822597, cc_library, ld.lld: error: undefined symbol: __errno + "libjemalloc5", // http://b/188503688, cc_library, `target: { android: { enabled: false } }` for android targets. } // Per-module denylist to opt modules out of mixed builds. Such modules will // still be generated via bp2build. mixedBuildsDisabledList = []string{ + "libc_common", // cparsons@ cc_library_static, depends on //bionic/libc:libc_nopthread + "libc_common_static", // cparsons@ cc_library_static, depends on //bionic/libc:libc_common + "libc_common_shared", // cparsons@ cc_library_static, depends on //bionic/libc:libc_common "libc_netbsd", // lberki@, cc_library_static, version script assignment of 'LIBC_PRIVATE' to symbol 'SHA1Final' failed: symbol not defined + "libc_nopthread", // cparsons@ cc_library_static, version script assignment of 'LIBC' to symbol 'memcmp' failed: symbol not defined "libc_openbsd", // ruperts@, cc_library_static, OK for bp2build but error: duplicate symbol: strcpy for mixed builds - "libsystemproperties", // cparsons@, cc_library_static, wrong include paths - "libpropertyinfoparser", // cparsons@, cc_library_static, wrong include paths "libarm-optimized-routines-string", // jingwen@, cc_library_static, OK for bp2build but b/186615213 (asflags not handled in bp2build), version script assignment of 'LIBC' to symbol 'memcmp' failed: symbol not defined (also for memrchr, strnlen) "fmtlib_ndk", // http://b/187040371, cc_library_static, OK for bp2build but format-inl.h:11:10: fatal error: 'cassert' file not found for mixed builds + "libc_nomalloc", // cc_library_static, OK for bp2build but ld.lld: error: undefined symbol: pthread_mutex_lock (and others) } // Used for quicker lookups - bp2buildDoNotWriteBuildFile = map[string]bool{} bp2buildModuleDoNotConvert = map[string]bool{} bp2buildCcLibraryStaticOnly = map[string]bool{} mixedBuildsDisabled = map[string]bool{} ) func init() { - for _, moduleName := range bp2buildDoNotWriteBuildFileList { - bp2buildDoNotWriteBuildFile[moduleName] = true - } - for _, moduleName := range bp2buildModuleDoNotConvertList { bp2buildModuleDoNotConvert[moduleName] = true } @@ -281,12 +255,21 @@ func GenerateCcLibraryStaticOnly(ctx BazelConversionPathContext) bool { return bp2buildCcLibraryStaticOnly[ctx.Module().Name()] } -func ShouldWriteBuildFileForDir(dir string) bool { - if _, ok := bp2buildDoNotWriteBuildFile[dir]; ok { - return false - } else { +func ShouldKeepExistingBuildFileForDir(dir string) bool { + if _, ok := bp2buildKeepExistingBuildFile[dir]; ok { + // Exact dir match return true } + // Check if subtree match + for prefix, recursive := range bp2buildKeepExistingBuildFile { + if recursive { + if strings.HasPrefix(dir, prefix+"/") { + return true + } + } + } + // Default + return false } // MixedBuildsEnabled checks that a module is ready to be replaced by a diff --git a/android/bazel_handler.go b/android/bazel_handler.go index 8cddbb2a3..a1206dc53 100644 --- a/android/bazel_handler.go +++ b/android/bazel_handler.go @@ -333,7 +333,7 @@ func (r *builtinBazelRunner) issueBazelCommand(paths *bazelPaths, runName bazel. // The actual platform values here may be overridden by configuration // transitions from the buildroot. cmdFlags = append(cmdFlags, - fmt.Sprintf("--platforms=%s", "//build/bazel/platforms:android_x86_64")) + fmt.Sprintf("--platforms=%s", "//build/bazel/platforms:android_arm")) cmdFlags = append(cmdFlags, fmt.Sprintf("--extra_toolchains=%s", "//prebuilts/clang/host/linux-x86:all")) // This should be parameterized on the host OS, but let's restrict to linux @@ -567,7 +567,7 @@ func (p *bazelPaths) intermediatesDir() string { // Returns the path where the contents of the @soong_injection repository live. // It is used by Soong to tell Bazel things it cannot over the command line. func (p *bazelPaths) injectedFilesDir() string { - return filepath.Join(p.buildDir, "soong_injection") + return filepath.Join(p.buildDir, bazel.SoongInjectionDirName) } // Returns the path of the synthetic Bazel workspace that contains a symlink diff --git a/android/config.go b/android/config.go index 79917adf4..da78c7a12 100644 --- a/android/config.go +++ b/android/config.go @@ -35,6 +35,7 @@ import ( "github.com/google/blueprint/proptools" "android/soong/android/soongconfig" + "android/soong/bazel" "android/soong/remoteexec" ) @@ -169,7 +170,7 @@ func loadConfig(config *config) error { // loadFromConfigFile loads and decodes configuration options from a JSON file // in the current working directory. -func loadFromConfigFile(configurable jsonConfigurable, filename string) error { +func loadFromConfigFile(configurable *productVariables, filename string) error { // Try to open the file configFileReader, err := os.Open(filename) defer configFileReader.Close() @@ -194,13 +195,20 @@ func loadFromConfigFile(configurable jsonConfigurable, filename string) error { } } - // No error - return nil + if Bool(configurable.GcovCoverage) && Bool(configurable.ClangCoverage) { + return fmt.Errorf("GcovCoverage and ClangCoverage cannot both be set") + } + + configurable.Native_coverage = proptools.BoolPtr( + Bool(configurable.GcovCoverage) || + Bool(configurable.ClangCoverage)) + + return saveToBazelConfigFile(configurable, filepath.Dir(filename)) } // atomically writes the config file in case two copies of soong_build are running simultaneously // (for example, docs generation and ninja manifest generation) -func saveToConfigFile(config jsonConfigurable, filename string) error { +func saveToConfigFile(config *productVariables, filename string) error { data, err := json.MarshalIndent(&config, "", " ") if err != nil { return fmt.Errorf("cannot marshal config data: %s", err.Error()) @@ -229,6 +237,35 @@ func saveToConfigFile(config jsonConfigurable, filename string) error { return nil } +func saveToBazelConfigFile(config *productVariables, outDir string) error { + dir := filepath.Join(outDir, bazel.SoongInjectionDirName, "product_config") + err := createDirIfNonexistent(dir, os.ModePerm) + if err != nil { + return fmt.Errorf("Could not create dir %s: %s", dir, err) + } + + data, err := json.MarshalIndent(&config, "", " ") + if err != nil { + return fmt.Errorf("cannot marshal config data: %s", err.Error()) + } + + bzl := []string{ + bazel.GeneratedBazelFileWarning, + fmt.Sprintf(`_product_vars = json.decode("""%s""")`, data), + "product_vars = _product_vars\n", + } + err = ioutil.WriteFile(filepath.Join(dir, "product_variables.bzl"), []byte(strings.Join(bzl, "\n")), 0644) + if err != nil { + return fmt.Errorf("Could not write .bzl config file %s", err) + } + err = ioutil.WriteFile(filepath.Join(dir, "BUILD"), []byte(bazel.GeneratedBazelFileWarning), 0644) + if err != nil { + return fmt.Errorf("Could not write BUILD config file %s", err) + } + + return nil +} + // NullConfig returns a mostly empty Config for use by standalone tools like dexpreopt_gen that // use the android package. func NullConfig(buildDir string) Config { @@ -448,14 +485,6 @@ func NewConfig(srcDir, buildDir string, moduleListFile string, availableEnv map[ config.AndroidFirstDeviceTarget = firstTarget(config.Targets[Android], "lib64", "lib32")[0] } - if Bool(config.productVariables.GcovCoverage) && Bool(config.productVariables.ClangCoverage) { - return Config{}, fmt.Errorf("GcovCoverage and ClangCoverage cannot both be set") - } - - config.productVariables.Native_coverage = proptools.BoolPtr( - Bool(config.productVariables.GcovCoverage) || - Bool(config.productVariables.ClangCoverage)) - config.BazelContext, err = NewBazelContext(config) config.bp2buildPackageConfig = bp2buildDefaultConfig config.bp2buildModuleTypeConfig = make(map[string]bool) @@ -1618,6 +1647,21 @@ func (l *ConfiguredJarList) RemoveList(list ConfiguredJarList) ConfiguredJarList return ConfiguredJarList{apexes, jars} } +// Filter keeps the entries if a jar appears in the given list of jars to keep; returns a new list. +func (l *ConfiguredJarList) Filter(jarsToKeep []string) ConfiguredJarList { + var apexes []string + var jars []string + + for i, jar := range l.jars { + if InList(jar, jarsToKeep) { + apexes = append(apexes, l.apexes[i]) + jars = append(jars, jar) + } + } + + return ConfiguredJarList{apexes, jars} +} + // CopyOfJars returns a copy of the list of strings containing jar module name // components. func (l *ConfiguredJarList) CopyOfJars() []string { diff --git a/android/defaults.go b/android/defaults.go index aacfbacc6..be80cf10f 100644 --- a/android/defaults.go +++ b/android/defaults.go @@ -104,6 +104,7 @@ type DefaultableHookContext interface { EarlyModuleContext CreateModule(ModuleFactory, ...interface{}) Module + AddMissingDependencies(missingDeps []string) } type DefaultableHook func(ctx DefaultableHookContext) diff --git a/android/module.go b/android/module.go index 9bc27a749..f745a4ab4 100644 --- a/android/module.go +++ b/android/module.go @@ -165,13 +165,20 @@ type BaseModuleContext interface { // OtherModuleDependencyVariantExists returns true if a module with the // specified name and variant exists. The variant must match the given // variations. It must also match all the non-local variations of the current - // module. In other words, it checks for the module AddVariationDependencies + // module. In other words, it checks for the module that AddVariationDependencies // would add a dependency on with the same arguments. OtherModuleDependencyVariantExists(variations []blueprint.Variation, name string) bool + // OtherModuleFarDependencyVariantExists returns true if a module with the + // specified name and variant exists. The variant must match the given + // variations, but not the non-local variations of the current module. In + // other words, it checks for the module that AddFarVariationDependencies + // would add a dependency on with the same arguments. + OtherModuleFarDependencyVariantExists(variations []blueprint.Variation, name string) bool + // OtherModuleReverseDependencyVariantExists returns true if a module with the // specified name exists with the same variations as the current module. In - // other words, it checks for the module AddReverseDependency would add a + // other words, it checks for the module that AddReverseDependency would add a // dependency on with the same argument. OtherModuleReverseDependencyVariantExists(name string) bool @@ -1500,7 +1507,7 @@ func (m *ModuleBase) computeInstallDeps(ctx ModuleContext) ([]*installPathsDepSe var installDeps []*installPathsDepSet var packagingSpecs []*packagingSpecsDepSet ctx.VisitDirectDeps(func(dep Module) { - if IsInstallDepNeeded(ctx.OtherModuleDependencyTag(dep)) { + if IsInstallDepNeeded(ctx.OtherModuleDependencyTag(dep)) && !dep.IsHideFromMake() { installDeps = append(installDeps, dep.base().installFilesDepSet) packagingSpecs = append(packagingSpecs, dep.base().packagingSpecsDepSet) } @@ -1809,8 +1816,8 @@ func (m *ModuleBase) GenerateBuildActions(blueprintCtx blueprint.ModuleContext) if m.Enabled() { // ensure all direct android.Module deps are enabled ctx.VisitDirectDepsBlueprint(func(bm blueprint.Module) { - if _, ok := bm.(Module); ok { - ctx.validateAndroidModule(bm, ctx.baseModuleContext.strictVisitDeps) + if m, ok := bm.(Module); ok { + ctx.validateAndroidModule(bm, ctx.OtherModuleDependencyTag(m), ctx.baseModuleContext.strictVisitDeps) } }) @@ -2022,6 +2029,9 @@ func (b *baseModuleContext) OtherModuleExists(name string) bool { return b.bp.Ot func (b *baseModuleContext) OtherModuleDependencyVariantExists(variations []blueprint.Variation, name string) bool { return b.bp.OtherModuleDependencyVariantExists(variations, name) } +func (b *baseModuleContext) OtherModuleFarDependencyVariantExists(variations []blueprint.Variation, name string) bool { + return b.bp.OtherModuleFarDependencyVariantExists(variations, name) +} func (b *baseModuleContext) OtherModuleReverseDependencyVariantExists(name string) bool { return b.bp.OtherModuleReverseDependencyVariantExists(name) } @@ -2234,7 +2244,12 @@ func (b *baseModuleContext) AddMissingDependencies(deps []string) { } } -func (b *baseModuleContext) validateAndroidModule(module blueprint.Module, strict bool) Module { +type AllowDisabledModuleDependency interface { + blueprint.DependencyTag + AllowDisabledModuleDependency(target Module) bool +} + +func (b *baseModuleContext) validateAndroidModule(module blueprint.Module, tag blueprint.DependencyTag, strict bool) Module { aModule, _ := module.(Module) if !strict { @@ -2247,10 +2262,12 @@ func (b *baseModuleContext) validateAndroidModule(module blueprint.Module, stric } if !aModule.Enabled() { - if b.Config().AllowMissingDependencies() { - b.AddMissingDependencies([]string{b.OtherModuleName(aModule)}) - } else { - b.ModuleErrorf("depends on disabled module %q", b.OtherModuleName(aModule)) + if t, ok := tag.(AllowDisabledModuleDependency); !ok || !t.AllowDisabledModuleDependency(aModule) { + if b.Config().AllowMissingDependencies() { + b.AddMissingDependencies([]string{b.OtherModuleName(aModule)}) + } else { + b.ModuleErrorf("depends on disabled module %q", b.OtherModuleName(aModule)) + } } return nil } @@ -2343,7 +2360,7 @@ func (b *baseModuleContext) VisitDirectDepsBlueprint(visit func(blueprint.Module func (b *baseModuleContext) VisitDirectDeps(visit func(Module)) { b.bp.VisitDirectDeps(func(module blueprint.Module) { - if aModule := b.validateAndroidModule(module, b.strictVisitDeps); aModule != nil { + if aModule := b.validateAndroidModule(module, b.bp.OtherModuleDependencyTag(module), b.strictVisitDeps); aModule != nil { visit(aModule) } }) @@ -2351,7 +2368,7 @@ func (b *baseModuleContext) VisitDirectDeps(visit func(Module)) { func (b *baseModuleContext) VisitDirectDepsWithTag(tag blueprint.DependencyTag, visit func(Module)) { b.bp.VisitDirectDeps(func(module blueprint.Module) { - if aModule := b.validateAndroidModule(module, b.strictVisitDeps); aModule != nil { + if aModule := b.validateAndroidModule(module, b.bp.OtherModuleDependencyTag(module), b.strictVisitDeps); aModule != nil { if b.bp.OtherModuleDependencyTag(aModule) == tag { visit(aModule) } @@ -2363,7 +2380,7 @@ func (b *baseModuleContext) VisitDirectDepsIf(pred func(Module) bool, visit func b.bp.VisitDirectDepsIf( // pred func(module blueprint.Module) bool { - if aModule := b.validateAndroidModule(module, b.strictVisitDeps); aModule != nil { + if aModule := b.validateAndroidModule(module, b.bp.OtherModuleDependencyTag(module), b.strictVisitDeps); aModule != nil { return pred(aModule) } else { return false @@ -2377,7 +2394,7 @@ func (b *baseModuleContext) VisitDirectDepsIf(pred func(Module) bool, visit func func (b *baseModuleContext) VisitDepsDepthFirst(visit func(Module)) { b.bp.VisitDepsDepthFirst(func(module blueprint.Module) { - if aModule := b.validateAndroidModule(module, b.strictVisitDeps); aModule != nil { + if aModule := b.validateAndroidModule(module, b.bp.OtherModuleDependencyTag(module), b.strictVisitDeps); aModule != nil { visit(aModule) } }) @@ -2387,7 +2404,7 @@ func (b *baseModuleContext) VisitDepsDepthFirstIf(pred func(Module) bool, visit b.bp.VisitDepsDepthFirstIf( // pred func(module blueprint.Module) bool { - if aModule := b.validateAndroidModule(module, b.strictVisitDeps); aModule != nil { + if aModule := b.validateAndroidModule(module, b.bp.OtherModuleDependencyTag(module), b.strictVisitDeps); aModule != nil { return pred(aModule) } else { return false diff --git a/android/packaging.go b/android/packaging.go index 72c0c1777..906582667 100644 --- a/android/packaging.go +++ b/android/packaging.go @@ -198,8 +198,8 @@ func (p *PackagingBase) AddDeps(ctx BottomUpMutatorContext, depTag blueprint.Dep } } -// See PackageModule.CopyDepsToZip -func (p *PackagingBase) CopyDepsToZip(ctx ModuleContext, zipOut WritablePath) (entries []string) { +// Returns transitive PackagingSpecs from deps +func (p *PackagingBase) GatherPackagingSpecs(ctx ModuleContext) map[string]PackagingSpec { m := make(map[string]PackagingSpec) ctx.VisitDirectDeps(func(child Module) { if pi, ok := ctx.OtherModuleDependencyTag(child).(PackagingItem); !ok || !pi.IsPackagingItem() { @@ -211,7 +211,12 @@ func (p *PackagingBase) CopyDepsToZip(ctx ModuleContext, zipOut WritablePath) (e } } }) + return m +} +// See PackageModule.CopyDepsToZip +func (p *PackagingBase) CopyDepsToZip(ctx ModuleContext, zipOut WritablePath) (entries []string) { + m := p.GatherPackagingSpecs(ctx) builder := NewRuleBuilder(pctx, ctx) dir := PathForModuleOut(ctx, ".zip") diff --git a/android/paths.go b/android/paths.go index 5d458cbb1..b192a357a 100644 --- a/android/paths.go +++ b/android/paths.go @@ -287,6 +287,17 @@ func (p OptionalPath) Path() Path { return p.path } +// AsPaths converts the OptionalPath into Paths. +// +// It returns nil if this is not valid, or a single length slice containing the Path embedded in +// this OptionalPath. +func (p OptionalPath) AsPaths() Paths { + if !p.valid { + return nil + } + return Paths{p.path} +} + // RelativeToTop returns an OptionalPath with the path that was embedded having been replaced by the // result of calling Path.RelativeToTop on it. func (p OptionalPath) RelativeToTop() OptionalPath { @@ -1979,6 +1990,10 @@ func RemoveAllOutputDir(path WritablePath) error { func CreateOutputDirIfNonexistent(path WritablePath, perm os.FileMode) error { dir := absolutePath(path.String()) + return createDirIfNonexistent(dir, perm) +} + +func createDirIfNonexistent(dir string, perm os.FileMode) error { if _, err := os.Stat(dir); os.IsNotExist(err) { return os.MkdirAll(dir, os.ModePerm) } else { @@ -1986,6 +2001,9 @@ func CreateOutputDirIfNonexistent(path WritablePath, perm os.FileMode) error { } } +// absolutePath is deliberately private so that Soong's Go plugins can't use it to find and +// read arbitrary files without going through the methods in the current package that track +// dependencies. func absolutePath(path string) string { if filepath.IsAbs(path) { return path diff --git a/android/paths_test.go b/android/paths_test.go index f8ccc7789..6f5d79e7e 100644 --- a/android/paths_test.go +++ b/android/paths_test.go @@ -141,6 +141,9 @@ func TestOptionalPath(t *testing.T) { path = OptionalPathForPath(nil) checkInvalidOptionalPath(t, path) + + path = OptionalPathForPath(PathForTesting("path")) + checkValidOptionalPath(t, path, "path") } func checkInvalidOptionalPath(t *testing.T, path OptionalPath) { @@ -151,6 +154,10 @@ func checkInvalidOptionalPath(t *testing.T, path OptionalPath) { if path.String() != "" { t.Errorf("Uninitialized OptionalPath String() should return \"\", not %q", path.String()) } + paths := path.AsPaths() + if len(paths) != 0 { + t.Errorf("Uninitialized OptionalPath AsPaths() should return empty Paths, not %q", paths) + } defer func() { if r := recover(); r == nil { t.Errorf("Expected a panic when calling Path() on an uninitialized OptionalPath") @@ -159,6 +166,21 @@ func checkInvalidOptionalPath(t *testing.T, path OptionalPath) { path.Path() } +func checkValidOptionalPath(t *testing.T, path OptionalPath, expectedString string) { + t.Helper() + if !path.Valid() { + t.Errorf("Initialized OptionalPath should not be invalid") + } + if path.String() != expectedString { + t.Errorf("Initialized OptionalPath String() should return %q, not %q", expectedString, path.String()) + } + paths := path.AsPaths() + if len(paths) != 1 { + t.Errorf("Initialized OptionalPath AsPaths() should return Paths with length 1, not %q", paths) + } + path.Path() +} + func check(t *testing.T, testType, testString string, got interface{}, err []error, expected interface{}, expectedErr []error) { diff --git a/android/variable.go b/android/variable.go index e83084525..672576a9f 100644 --- a/android/variable.go +++ b/android/variable.go @@ -282,7 +282,7 @@ type productVariables struct { NativeCoverageExcludePaths []string `json:",omitempty"` // Set by NewConfig - Native_coverage *bool + Native_coverage *bool `json:",omitempty"` SanitizeHost []string `json:",omitempty"` SanitizeDevice []string `json:",omitempty"` diff --git a/apex/Android.bp b/apex/Android.bp index e234181d3..14c877100 100644 --- a/apex/Android.bp +++ b/apex/Android.bp @@ -32,6 +32,7 @@ bootstrap_go_package { "apex_test.go", "bootclasspath_fragment_test.go", "platform_bootclasspath_test.go", + "systemserver_classpath_fragment_test.go", "vndk_test.go", ], pluginFor: ["soong_build"], diff --git a/apex/apex.go b/apex/apex.go index 2c0df2785..34483272e 100644 --- a/apex/apex.go +++ b/apex/apex.go @@ -69,10 +69,12 @@ func RegisterPostDepsMutators(ctx android.RegisterMutatorsContext) { ctx.BottomUp("apex_unique", apexUniqueVariationsMutator).Parallel() ctx.BottomUp("apex_test_for_deps", apexTestForDepsMutator).Parallel() ctx.BottomUp("apex_test_for", apexTestForMutator).Parallel() + // Run mark_platform_availability before the apexMutator as the apexMutator needs to know whether + // it should create a platform variant. + ctx.BottomUp("mark_platform_availability", markPlatformAvailability).Parallel() ctx.BottomUp("apex", apexMutator).Parallel() ctx.BottomUp("apex_directly_in_any", apexDirectlyInAnyMutator).Parallel() ctx.BottomUp("apex_flattened", apexFlattenedMutator).Parallel() - ctx.BottomUp("mark_platform_availability", markPlatformAvailability).Parallel() } type apexBundleProperties struct { @@ -100,6 +102,9 @@ type apexBundleProperties struct { // List of bootclasspath fragments that are embedded inside this APEX bundle. Bootclasspath_fragments []string + // List of systemserverclasspath fragments that are embedded inside this APEX bundle. + Systemserverclasspath_fragments []string + // List of java libraries that are embedded inside this APEX bundle. Java_libs []string @@ -573,6 +578,7 @@ var ( executableTag = dependencyTag{name: "executable", payload: true} fsTag = dependencyTag{name: "filesystem", payload: true} bcpfTag = dependencyTag{name: "bootclasspathFragment", payload: true, sourceOnly: true} + sscpfTag = dependencyTag{name: "systemserverclasspathFragment", payload: true, sourceOnly: true} compatConfigTag = dependencyTag{name: "compatConfig", payload: true, sourceOnly: true} javaLibTag = dependencyTag{name: "javaLib", payload: true} jniLibTag = dependencyTag{name: "jniLib", payload: true} @@ -735,24 +741,27 @@ func (a *apexBundle) DepsMutator(ctx android.BottomUpMutatorContext) { } } - // For prebuilt_etc, use the first variant (64 on 64/32bit device, 32 on 32bit device) - // regardless of the TARGET_PREFER_* setting. See b/144532908 - archForPrebuiltEtc := config.Arches()[0] - for _, arch := range config.Arches() { - // Prefer 64-bit arch if there is any - if arch.ArchType.Multilib == "lib64" { - archForPrebuiltEtc = arch - break + if prebuilts := a.properties.Prebuilts; len(prebuilts) > 0 { + // For prebuilt_etc, use the first variant (64 on 64/32bit device, 32 on 32bit device) + // regardless of the TARGET_PREFER_* setting. See b/144532908 + archForPrebuiltEtc := config.Arches()[0] + for _, arch := range config.Arches() { + // Prefer 64-bit arch if there is any + if arch.ArchType.Multilib == "lib64" { + archForPrebuiltEtc = arch + break + } } + ctx.AddFarVariationDependencies([]blueprint.Variation{ + {Mutator: "os", Variation: ctx.Os().String()}, + {Mutator: "arch", Variation: archForPrebuiltEtc.String()}, + }, prebuiltTag, prebuilts...) } - ctx.AddFarVariationDependencies([]blueprint.Variation{ - {Mutator: "os", Variation: ctx.Os().String()}, - {Mutator: "arch", Variation: archForPrebuiltEtc.String()}, - }, prebuiltTag, a.properties.Prebuilts...) // Common-arch dependencies come next commonVariation := ctx.Config().AndroidCommonTarget.Variations() ctx.AddFarVariationDependencies(commonVariation, bcpfTag, a.properties.Bootclasspath_fragments...) + ctx.AddFarVariationDependencies(commonVariation, sscpfTag, a.properties.Systemserverclasspath_fragments...) ctx.AddFarVariationDependencies(commonVariation, javaLibTag, a.properties.Java_libs...) ctx.AddFarVariationDependencies(commonVariation, bpfTag, a.properties.Bpfs...) ctx.AddFarVariationDependencies(commonVariation, fsTag, a.properties.Filesystems...) @@ -899,12 +908,16 @@ func (a *apexBundle) ApexInfoMutator(mctx android.TopDownMutatorContext) { // This is the main part of this mutator. Mark the collected dependencies that they need to // be built for this apexBundle. + + // Note that there are many different names. + // ApexVariationName: this is the name of the apex variation apexInfo := android.ApexInfo{ - ApexVariationName: mctx.ModuleName(), + ApexVariationName: mctx.ModuleName(), // could be com.android.foo MinSdkVersion: minSdkVersion, RequiredSdks: a.RequiredSdks(), Updatable: a.Updatable(), - InApexes: []string{mctx.ModuleName()}, + InApexVariants: []string{mctx.ModuleName()}, // could be com.android.foo + InApexModules: []string{a.Name()}, // could be com.mycompany.android.foo ApexContents: []*android.ApexContents{apexContents}, } mctx.WalkDeps(func(child, parent android.Module) bool { @@ -1013,9 +1026,8 @@ func markPlatformAvailability(mctx android.BottomUpMutatorContext) { } }) - // Exception 1: stub libraries and native bridge libraries are always available to platform - if cc, ok := mctx.Module().(*cc.Module); ok && - (cc.IsStubs() || cc.Target().NativeBridge == android.NativeBridgeEnabled) { + // Exception 1: check to see if the module always requires it. + if am.AlwaysRequiresPlatformApexVariant() { availableToPlatform = true } @@ -1256,7 +1268,7 @@ func (a *apexBundle) IsNativeCoverageNeeded(ctx android.BaseModuleContext) bool } // Implements cc.Coverage -func (a *apexBundle) PreventInstall() { +func (a *apexBundle) SetPreventInstall() { a.properties.PreventInstall = true } @@ -1598,7 +1610,7 @@ func (a *apexBundle) WalkPayloadDeps(ctx android.ModuleContext, do android.Paylo } ai := ctx.OtherModuleProvider(child, android.ApexInfoProvider).(android.ApexInfo) - externalDep := !android.InList(ctx.ModuleName(), ai.InApexes) + externalDep := !android.InList(ctx.ModuleName(), ai.InApexVariants) // Visit actually return do(ctx, parent, am, externalDep) @@ -1714,6 +1726,15 @@ func (a *apexBundle) GenerateAndroidBuildActions(ctx android.ModuleContext) { filesInfo = append(filesInfo, filesToAdd...) return true } + case sscpfTag: + { + if _, ok := child.(*java.SystemServerClasspathModule); !ok { + ctx.PropertyErrorf("systemserverclasspath_fragments", "%q is not a systemserverclasspath_fragment module", depName) + return false + } + filesInfo = append(filesInfo, apexClasspathFragmentProtoFile(ctx, child)) + return true + } case javaLibTag: switch child.(type) { case *java.Library, *java.SdkLibrary, *java.DexImport, *java.SdkLibraryImport, *java.Import: @@ -1940,7 +1961,16 @@ func (a *apexBundle) GenerateAndroidBuildActions(ctx android.ModuleContext) { default: ctx.PropertyErrorf("bootclasspath_fragments", "bootclasspath_fragment content %q of type %q is not supported", depName, ctx.OtherModuleType(child)) } - + } else if java.IsSystemServerClasspathFragmentContentDepTag(depTag) { + // Add the contents of the systemserverclasspath fragment to the apex. + switch child.(type) { + case *java.Library, *java.SdkLibrary: + af := apexFileForJavaModule(ctx, child.(javaModule)) + filesInfo = append(filesInfo, af) + return true // track transitive dependencies + default: + ctx.PropertyErrorf("systemserverclasspath_fragments", "systemserverclasspath_fragment content %q of type %q is not supported", depName, ctx.OtherModuleType(child)) + } } else if _, ok := depTag.(android.CopyDirectlyInAnyApexTag); ok { // nothing } else if am.CanHaveApexVariants() && am.IsInstallableToApex() { @@ -1979,7 +2009,9 @@ func (a *apexBundle) GenerateAndroidBuildActions(ctx android.ModuleContext) { // Sort to have consistent build rules sort.Slice(filesInfo, func(i, j int) bool { - return filesInfo[i].builtFile.String() < filesInfo[j].builtFile.String() + // Sort by destination path so as to ensure consistent ordering even if the source of the files + // changes. + return filesInfo[i].path() < filesInfo[j].path() }) //////////////////////////////////////////////////////////////////////////////////////////// @@ -2103,13 +2135,19 @@ func apexBootclasspathFragmentFiles(ctx android.ModuleContext, module blueprint. } // Add classpaths.proto config. - classpathProtoOutput := bootclasspathFragmentInfo.ClasspathFragmentProtoOutput - classpathProto := newApexFile(ctx, classpathProtoOutput, classpathProtoOutput.Base(), bootclasspathFragmentInfo.ClasspathFragmentProtoInstallDir.Rel(), etc, nil) - filesToAdd = append(filesToAdd, classpathProto) + filesToAdd = append(filesToAdd, apexClasspathFragmentProtoFile(ctx, module)) return filesToAdd } +// apexClasspathFragmentProtoFile returns apexFile structure defining the classpath.proto config that +// the module contributes to the apex. +func apexClasspathFragmentProtoFile(ctx android.ModuleContext, module blueprint.Module) apexFile { + fragmentInfo := ctx.OtherModuleProvider(module, java.ClasspathFragmentProtoContentInfoProvider).(java.ClasspathFragmentProtoContentInfo) + classpathProtoOutput := fragmentInfo.ClasspathFragmentProtoOutput + return newApexFile(ctx, classpathProtoOutput, classpathProtoOutput.Base(), fragmentInfo.ClasspathFragmentProtoInstallDir.Rel(), etc, nil) +} + // apexFileForBootclasspathFragmentContentModule creates an apexFile for a bootclasspath_fragment // content module, i.e. a library that is part of the bootclasspath. func apexFileForBootclasspathFragmentContentModule(ctx android.ModuleContext, fragmentModule blueprint.Module, javaModule javaModule) apexFile { @@ -2117,7 +2155,10 @@ func apexFileForBootclasspathFragmentContentModule(ctx android.ModuleContext, fr // Get the dexBootJar from the bootclasspath_fragment as that is responsible for performing the // hidden API encpding. - dexBootJar := bootclasspathFragmentInfo.DexBootJarPathForContentModule(javaModule) + dexBootJar, err := bootclasspathFragmentInfo.DexBootJarPathForContentModule(javaModule) + if err != nil { + ctx.ModuleErrorf("%s", err) + } // Create an apexFile as for a normal java module but with the dex boot jar provided by the // bootclasspath_fragment. diff --git a/apex/apex_test.go b/apex/apex_test.go index 08d82e90c..364013fbe 100644 --- a/apex/apex_test.go +++ b/apex/apex_test.go @@ -4542,11 +4542,16 @@ func TestPrebuiltExportDexImplementationJars(t *testing.T) { } func TestBootDexJarsFromSourcesAndPrebuilts(t *testing.T) { - preparer := java.FixtureConfigureBootJars("myapex:libfoo", "myapex:libbar") + preparer := android.GroupFixturePreparers( + java.FixtureConfigureBootJars("myapex:libfoo", "myapex:libbar"), + // Make sure that the frameworks/base/Android.bp file exists as otherwise hidden API encoding + // is disabled. + android.FixtureAddTextFile("frameworks/base/Android.bp", ""), + ) checkBootDexJarPath := func(t *testing.T, ctx *android.TestContext, stem string, bootDexJarPath string) { t.Helper() - s := ctx.SingletonForTests("dex_bootjars") + s := ctx.ModuleForTests("platform-bootclasspath", "android_common") foundLibfooJar := false base := stem + ".jar" for _, output := range s.AllOutputs() { @@ -4564,7 +4569,7 @@ func TestBootDexJarsFromSourcesAndPrebuilts(t *testing.T) { checkHiddenAPIIndexInputs := func(t *testing.T, ctx *android.TestContext, expectedInputs string) { t.Helper() platformBootclasspath := ctx.ModuleForTests("platform-bootclasspath", "android_common") - indexRule := platformBootclasspath.Rule("platform-bootclasspath-monolithic-hiddenapi-index") + indexRule := platformBootclasspath.Rule("monolithic_hidden_API_index") java.CheckHiddenAPIRuleInputs(t, expectedInputs, indexRule) } @@ -4602,10 +4607,10 @@ func TestBootDexJarsFromSourcesAndPrebuilts(t *testing.T) { checkBootDexJarPath(t, ctx, "libfoo", "out/soong/.intermediates/myapex.deapexer/android_common/deapexer/javalib/libfoo.jar") checkBootDexJarPath(t, ctx, "libbar", "out/soong/.intermediates/myapex.deapexer/android_common/deapexer/javalib/libbar.jar") - // Make sure that the dex file from the prebuilt_apex contributes to the hiddenapi index file. + // Verify the correct module jars contribute to the hiddenapi index file. checkHiddenAPIIndexInputs(t, ctx, ` -.intermediates/libbar/android_common_myapex/hiddenapi/index.csv -.intermediates/libfoo/android_common_myapex/hiddenapi/index.csv +.intermediates/libbar.stubs/android_common/combined/libbar.stubs.jar +.intermediates/libfoo/android_common_myapex/combined/libfoo.jar `) }) @@ -4636,10 +4641,10 @@ func TestBootDexJarsFromSourcesAndPrebuilts(t *testing.T) { checkBootDexJarPath(t, ctx, "libfoo", "out/soong/.intermediates/myapex.deapexer/android_common/deapexer/javalib/libfoo.jar") checkBootDexJarPath(t, ctx, "libbar", "out/soong/.intermediates/myapex.deapexer/android_common/deapexer/javalib/libbar.jar") - // Make sure that the dex file from the apex_set contributes to the hiddenapi index file. + // Verify the correct module jars contribute to the hiddenapi index file. checkHiddenAPIIndexInputs(t, ctx, ` -.intermediates/libbar/android_common_myapex/hiddenapi/index.csv -.intermediates/libfoo/android_common_myapex/hiddenapi/index.csv +.intermediates/libbar.stubs/android_common/combined/libbar.stubs.jar +.intermediates/libfoo/android_common_myapex/combined/libfoo.jar `) }) @@ -4743,10 +4748,10 @@ func TestBootDexJarsFromSourcesAndPrebuilts(t *testing.T) { checkBootDexJarPath(t, ctx, "libfoo", "out/soong/.intermediates/myapex.deapexer/android_common/deapexer/javalib/libfoo.jar") checkBootDexJarPath(t, ctx, "libbar", "out/soong/.intermediates/myapex.deapexer/android_common/deapexer/javalib/libbar.jar") - // Make sure that the dex file from the prebuilt_apex contributes to the hiddenapi index file. + // Verify the correct module jars contribute to the hiddenapi index file. checkHiddenAPIIndexInputs(t, ctx, ` -.intermediates/prebuilt_libbar/android_common_myapex/hiddenapi/index.csv -.intermediates/prebuilt_libfoo/android_common_myapex/hiddenapi/index.csv +.intermediates/prebuilt_libbar.stubs/android_common/combined/libbar.stubs.jar +.intermediates/prebuilt_libfoo/android_common_myapex/combined/libfoo.jar `) }) @@ -4810,10 +4815,10 @@ func TestBootDexJarsFromSourcesAndPrebuilts(t *testing.T) { checkBootDexJarPath(t, ctx, "libfoo", "out/soong/.intermediates/libfoo/android_common_apex10000/hiddenapi/libfoo.jar") checkBootDexJarPath(t, ctx, "libbar", "out/soong/.intermediates/libbar/android_common_myapex/hiddenapi/libbar.jar") - // Make sure that the dex file from the prebuilt_apex contributes to the hiddenapi index file. + // Verify the correct module jars contribute to the hiddenapi index file. checkHiddenAPIIndexInputs(t, ctx, ` -.intermediates/libbar/android_common_myapex/hiddenapi/index.csv -.intermediates/libfoo/android_common_apex10000/hiddenapi/index.csv +.intermediates/libbar/android_common_myapex/javac/libbar.jar +.intermediates/libfoo/android_common_apex10000/javac/libfoo.jar `) }) @@ -4879,10 +4884,10 @@ func TestBootDexJarsFromSourcesAndPrebuilts(t *testing.T) { checkBootDexJarPath(t, ctx, "libfoo", "out/soong/.intermediates/myapex.deapexer/android_common/deapexer/javalib/libfoo.jar") checkBootDexJarPath(t, ctx, "libbar", "out/soong/.intermediates/myapex.deapexer/android_common/deapexer/javalib/libbar.jar") - // Make sure that the dex file from the prebuilt_apex contributes to the hiddenapi index file. + // Verify the correct module jars contribute to the hiddenapi index file. checkHiddenAPIIndexInputs(t, ctx, ` -.intermediates/prebuilt_libbar/android_common_myapex/hiddenapi/index.csv -.intermediates/prebuilt_libfoo/android_common_myapex/hiddenapi/index.csv +.intermediates/prebuilt_libbar.stubs/android_common/combined/libbar.stubs.jar +.intermediates/prebuilt_libfoo/android_common_myapex/combined/libfoo.jar `) }) } @@ -7584,6 +7589,28 @@ func TestPrebuiltStubLibDep(t *testing.T) { } } +func TestHostApexInHostOnlyBuild(t *testing.T) { + testApex(t, ` + apex { + name: "myapex", + host_supported: true, + key: "myapex.key", + updatable: false, + payload_type: "zip", + } + apex_key { + name: "myapex.key", + public_key: "testkey.avbpubkey", + private_key: "testkey.pem", + } + `, + android.FixtureModifyConfig(func(config android.Config) { + // We may not have device targets in all builds, e.g. in + // prebuilts/build-tools/build-prebuilts.sh + config.Targets[android.Android] = []android.Target{} + })) +} + func TestMain(m *testing.M) { os.Exit(m.Run()) } diff --git a/apex/bootclasspath_fragment_test.go b/apex/bootclasspath_fragment_test.go index 7bb3ff611..9965f83bd 100644 --- a/apex/bootclasspath_fragment_test.go +++ b/apex/bootclasspath_fragment_test.go @@ -141,6 +141,128 @@ test_device/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot-quuz.vde `) } +func TestBootclasspathFragments_FragmentDependency(t *testing.T) { + result := android.GroupFixturePreparers( + prepareForTestWithBootclasspathFragment, + // Configure some libraries in the art bootclasspath_fragment and platform_bootclasspath. + java.FixtureConfigureBootJars("com.android.art:baz", "com.android.art:quuz", "platform:foo", "platform:bar"), + prepareForTestWithArtApex, + + java.PrepareForTestWithJavaSdkLibraryFiles, + java.FixtureWithLastReleaseApis("foo", "baz"), + ).RunTestWithBp(t, ` + java_sdk_library { + name: "foo", + srcs: ["b.java"], + shared_library: false, + public: { + enabled: true, + }, + system: { + enabled: true, + }, + } + + java_library { + name: "bar", + srcs: ["b.java"], + installable: true, + } + + apex { + name: "com.android.art", + key: "com.android.art.key", + bootclasspath_fragments: ["art-bootclasspath-fragment"], + updatable: false, + } + + apex_key { + name: "com.android.art.key", + public_key: "com.android.art.avbpubkey", + private_key: "com.android.art.pem", + } + + java_sdk_library { + name: "baz", + apex_available: [ + "com.android.art", + ], + srcs: ["b.java"], + shared_library: false, + public: { + enabled: true, + }, + system: { + enabled: true, + }, + test: { + enabled: true, + }, + } + + java_library { + name: "quuz", + apex_available: [ + "com.android.art", + ], + srcs: ["b.java"], + compile_dex: true, + } + + bootclasspath_fragment { + name: "art-bootclasspath-fragment", + image_name: "art", + // Must match the "com.android.art:" entries passed to FixtureConfigureBootJars above. + contents: ["baz", "quuz"], + apex_available: [ + "com.android.art", + ], + } + + bootclasspath_fragment { + name: "other-bootclasspath-fragment", + contents: ["foo", "bar"], + fragments: [ + { + apex: "com.android.art", + module: "art-bootclasspath-fragment", + }, + ], + } +`, + ) + + checkSdkKindStubs := func(message string, info java.HiddenAPIInfo, kind android.SdkKind, expectedPaths ...string) { + t.Helper() + android.AssertPathsRelativeToTopEquals(t, fmt.Sprintf("%s %s", message, kind), expectedPaths, info.TransitiveStubDexJarsByKind[kind]) + } + + // Check stub dex paths exported by art. + artFragment := result.Module("art-bootclasspath-fragment", "android_common") + artInfo := result.ModuleProvider(artFragment, java.HiddenAPIInfoProvider).(java.HiddenAPIInfo) + + bazPublicStubs := "out/soong/.intermediates/baz.stubs/android_common/dex/baz.stubs.jar" + bazSystemStubs := "out/soong/.intermediates/baz.stubs.system/android_common/dex/baz.stubs.system.jar" + bazTestStubs := "out/soong/.intermediates/baz.stubs.test/android_common/dex/baz.stubs.test.jar" + + checkSdkKindStubs("art", artInfo, android.SdkPublic, bazPublicStubs) + checkSdkKindStubs("art", artInfo, android.SdkSystem, bazSystemStubs) + checkSdkKindStubs("art", artInfo, android.SdkTest, bazTestStubs) + checkSdkKindStubs("art", artInfo, android.SdkCorePlatform) + + // Check stub dex paths exported by other. + otherFragment := result.Module("other-bootclasspath-fragment", "android_common") + otherInfo := result.ModuleProvider(otherFragment, java.HiddenAPIInfoProvider).(java.HiddenAPIInfo) + + fooPublicStubs := "out/soong/.intermediates/foo.stubs/android_common/dex/foo.stubs.jar" + fooSystemStubs := "out/soong/.intermediates/foo.stubs.system/android_common/dex/foo.stubs.system.jar" + + checkSdkKindStubs("other", otherInfo, android.SdkPublic, bazPublicStubs, fooPublicStubs) + checkSdkKindStubs("other", otherInfo, android.SdkSystem, bazSystemStubs, fooSystemStubs) + checkSdkKindStubs("other", otherInfo, android.SdkTest, bazTestStubs, fooSystemStubs) + checkSdkKindStubs("other", otherInfo, android.SdkCorePlatform) +} + func checkBootclasspathFragment(t *testing.T, result *android.TestResult, moduleName string, expectedConfiguredModules string, expectedBootclasspathFragmentFiles string) { t.Helper() @@ -433,6 +555,14 @@ func TestBootclasspathFragmentContentsNoName(t *testing.T) { result := android.GroupFixturePreparers( prepareForTestWithBootclasspathFragment, prepareForTestWithMyapex, + // Configure bootclasspath jars to ensure that hidden API encoding is performed on them. + java.FixtureConfigureBootJars("myapex:foo", "myapex:bar"), + // Make sure that the frameworks/base/Android.bp file exists as otherwise hidden API encoding + // is disabled. + android.FixtureAddTextFile("frameworks/base/Android.bp", ""), + + java.PrepareForTestWithJavaSdkLibraryFiles, + java.FixtureWithLastReleaseApis("foo"), ).RunTestWithBp(t, ` apex { name: "myapex", @@ -449,10 +579,11 @@ func TestBootclasspathFragmentContentsNoName(t *testing.T) { private_key: "testkey.pem", } - java_library { + java_sdk_library { name: "foo", srcs: ["b.java"], - installable: true, + shared_library: false, + public: {enabled: true}, apex_available: [ "myapex", ], @@ -491,6 +622,30 @@ func TestBootclasspathFragmentContentsNoName(t *testing.T) { `myapex.key`, `mybootclasspathfragment`, }) + + apex := result.ModuleForTests("myapex", "android_common_myapex_image") + apexRule := apex.Rule("apexRule") + copyCommands := apexRule.Args["copy_commands"] + + // Make sure that the fragment provides the hidden API encoded dex jars to the APEX. + fragment := result.Module("mybootclasspathfragment", "android_common_apex10000") + + info := result.ModuleProvider(fragment, java.BootclasspathFragmentApexContentInfoProvider).(java.BootclasspathFragmentApexContentInfo) + + checkFragmentExportedDexJar := func(name string, expectedDexJar string) { + module := result.Module(name, "android_common_apex10000") + dexJar, err := info.DexBootJarPathForContentModule(module) + if err != nil { + t.Error(err) + } + android.AssertPathRelativeToTopEquals(t, name+" dex", expectedDexJar, dexJar) + + expectedCopyCommand := fmt.Sprintf("&& cp -f %s out/soong/.intermediates/myapex/android_common_myapex_image/image.apex/javalib/%s.jar", expectedDexJar, name) + android.AssertStringDoesContain(t, name+" apex copy command", copyCommands, expectedCopyCommand) + } + + checkFragmentExportedDexJar("foo", "out/soong/.intermediates/mybootclasspathfragment/android_common_apex10000/hiddenapi-modular/encoded/foo.jar") + checkFragmentExportedDexJar("bar", "out/soong/.intermediates/mybootclasspathfragment/android_common_apex10000/hiddenapi-modular/encoded/bar.jar") } // TODO(b/177892522) - add test for host apex. diff --git a/apex/platform_bootclasspath_test.go b/apex/platform_bootclasspath_test.go index 52b1689ba..ce12f4635 100644 --- a/apex/platform_bootclasspath_test.go +++ b/apex/platform_bootclasspath_test.go @@ -20,6 +20,7 @@ import ( "android/soong/android" "android/soong/java" "github.com/google/blueprint" + "github.com/google/blueprint/proptools" ) // Contains tests for platform_bootclasspath logic from java/platform_bootclasspath.go that requires @@ -174,6 +175,141 @@ func TestPlatformBootclasspathDependencies(t *testing.T) { }) } +// TestPlatformBootclasspath_AlwaysUsePrebuiltSdks verifies that the build does not fail when +// AlwaysUsePrebuiltSdk() returns true. The structure of the modules in this test matches what +// currently exists in some places in the Android build but it is not the intended structure. It is +// in fact an invalid structure that should cause build failures. However, fixing that structure +// will take too long so in the meantime this tests the workarounds to avoid build breakages. +// +// The main issues with this structure are: +// 1. There is no prebuilt_bootclasspath_fragment referencing the "foo" java_sdk_library_import. +// 2. There is no prebuilt_apex/apex_set which makes the dex implementation jar available to the +// prebuilt_bootclasspath_fragment and the "foo" java_sdk_library_import. +// +// Together these cause the following symptoms: +// 1. The "foo" java_sdk_library_import does not have a dex implementation jar. +// 2. The "foo" java_sdk_library_import does not have a myapex variant. +// +// TODO(b/179354495): Fix the structure in this test once the main Android build has been fixed. +func TestPlatformBootclasspath_AlwaysUsePrebuiltSdks(t *testing.T) { + result := android.GroupFixturePreparers( + prepareForTestWithPlatformBootclasspath, + prepareForTestWithMyapex, + // Configure two libraries, the first is a java_sdk_library whose prebuilt will be used because + // of AlwaysUsePrebuiltsSdk() but does not have an appropriate apex variant and does not provide + // a boot dex jar. The second is a normal library that is unaffected. The order matters because + // if the dependency on myapex:foo is filtered out because of either of those conditions then + // the dependencies resolved by the platform_bootclasspath will not match the configured list + // and so will fail the test. + java.FixtureConfigureUpdatableBootJars("myapex:foo", "myapex:bar"), + java.PrepareForTestWithJavaSdkLibraryFiles, + android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { + variables.Always_use_prebuilt_sdks = proptools.BoolPtr(true) + }), + java.FixtureWithPrebuiltApis(map[string][]string{ + "current": {}, + "30": {"foo"}, + }), + ).RunTestWithBp(t, ` + apex { + name: "myapex", + key: "myapex.key", + bootclasspath_fragments: [ + "mybootclasspath-fragment", + ], + updatable: false, + } + + apex_key { + name: "myapex.key", + public_key: "testkey.avbpubkey", + private_key: "testkey.pem", + } + + java_library { + name: "bar", + srcs: ["b.java"], + installable: true, + apex_available: ["myapex"], + permitted_packages: ["bar"], + } + + java_sdk_library { + name: "foo", + srcs: ["b.java"], + shared_library: false, + public: { + enabled: true, + }, + apex_available: ["myapex"], + permitted_packages: ["foo"], + } + + // A prebuilt java_sdk_library_import that is not preferred by default but will be preferred + // because AlwaysUsePrebuiltSdks() is true. + java_sdk_library_import { + name: "foo", + prefer: false, + shared_library: false, + public: { + jars: ["sdk_library/public/foo-stubs.jar"], + stub_srcs: ["sdk_library/public/foo_stub_sources"], + current_api: "sdk_library/public/foo.txt", + removed_api: "sdk_library/public/foo-removed.txt", + sdk_version: "current", + }, + apex_available: ["myapex"], + } + + // This always depends on the source foo module, its dependencies are not affected by the + // AlwaysUsePrebuiltSdks(). + bootclasspath_fragment { + name: "mybootclasspath-fragment", + apex_available: [ + "myapex", + ], + contents: [ + "foo", "bar", + ], + } + + platform_bootclasspath { + name: "myplatform-bootclasspath", + } +`, + ) + + java.CheckPlatformBootclasspathModules(t, result, "myplatform-bootclasspath", []string{ + // The configured contents of BootJars. + "platform:prebuilt_foo", // Note: This is the platform not myapex variant. + "myapex:bar", + }) + + // Make sure that the myplatform-bootclasspath has the correct dependencies. + CheckModuleDependencies(t, result.TestContext, "myplatform-bootclasspath", "android_common", []string{ + // The following are stubs. + "platform:prebuilt_sdk_public_current_android", + "platform:prebuilt_sdk_system_current_android", + "platform:prebuilt_sdk_test_current_android", + + // Not a prebuilt as no prebuilt existed when it was added. + "platform:legacy.core.platform.api.stubs", + + // Needed for generating the boot image. + `platform:dex2oatd`, + + // The platform_bootclasspath intentionally adds dependencies on both source and prebuilt + // modules when available as it does not know which one will be preferred. + // + // The source module has an APEX variant but the prebuilt does not. + "myapex:foo", + "platform:prebuilt_foo", + + // Only a source module exists. + "myapex:bar", + }) +} + // CheckModuleDependencies checks the dependencies of the selected module against the expected list. // // The expected list must be a list of strings of the form "<apex>:<module>", where <apex> is the diff --git a/apex/prebuilt.go b/apex/prebuilt.go index 9d632a9b1..6125ef50f 100644 --- a/apex/prebuilt.go +++ b/apex/prebuilt.go @@ -230,7 +230,8 @@ func (p *prebuiltCommon) apexInfoMutator(mctx android.TopDownMutatorContext) { // Create an ApexInfo for the prebuilt_apex. apexInfo := android.ApexInfo{ ApexVariationName: android.RemoveOptionalPrebuiltPrefix(mctx.ModuleName()), - InApexes: []string{mctx.ModuleName()}, + InApexVariants: []string{mctx.ModuleName()}, + InApexModules: []string{mctx.ModuleName()}, ApexContents: []*android.ApexContents{apexContents}, ForPrebuiltApex: true, } diff --git a/apex/systemserver_classpath_fragment_test.go b/apex/systemserver_classpath_fragment_test.go new file mode 100644 index 000000000..e1a101ad9 --- /dev/null +++ b/apex/systemserver_classpath_fragment_test.go @@ -0,0 +1,78 @@ +// Copyright (C) 2021 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package apex + +import ( + "testing" + + "android/soong/android" + "android/soong/java" +) + +var prepareForTestWithSystemserverclasspathFragment = android.GroupFixturePreparers( + java.PrepareForTestWithDexpreopt, + PrepareForTestWithApexBuildComponents, +) + +func TestSystemserverclasspathFragmentContents(t *testing.T) { + result := android.GroupFixturePreparers( + prepareForTestWithSystemserverclasspathFragment, + prepareForTestWithMyapex, + ).RunTestWithBp(t, ` + apex { + name: "myapex", + key: "myapex.key", + systemserverclasspath_fragments: [ + "mysystemserverclasspathfragment", + ], + updatable: false, + } + + apex_key { + name: "myapex.key", + public_key: "testkey.avbpubkey", + private_key: "testkey.pem", + } + + java_library { + name: "foo", + srcs: ["b.java"], + installable: true, + apex_available: [ + "myapex", + ], + } + + systemserverclasspath_fragment { + name: "mysystemserverclasspathfragment", + contents: [ + "foo", + ], + apex_available: [ + "myapex", + ], + } + `) + + ensureExactContents(t, result.TestContext, "myapex", "android_common_myapex_image", []string{ + "etc/classpaths/mysystemserverclasspathfragment.pb", + "javalib/foo.jar", + }) + + java.CheckModuleDependencies(t, result.TestContext, "myapex", "android_common_myapex_image", []string{ + `myapex.key`, + `mysystemserverclasspathfragment`, + }) +} diff --git a/bazel/aquery.go b/bazel/aquery.go index 555f1dcb4..bda3a1ae6 100644 --- a/bazel/aquery.go +++ b/bazel/aquery.go @@ -81,23 +81,30 @@ type BuildStatement struct { Mnemonic string } -// AqueryBuildStatements returns an array of BuildStatements which should be registered (and output -// to a ninja file) to correspond one-to-one with the given action graph json proto (from a bazel -// aquery invocation). -func AqueryBuildStatements(aqueryJsonProto []byte) ([]BuildStatement, error) { - buildStatements := []BuildStatement{} - - var aqueryResult actionGraphContainer - err := json.Unmarshal(aqueryJsonProto, &aqueryResult) - - if err != nil { - return nil, err - } +// A helper type for aquery processing which facilitates retrieval of path IDs from their +// less readable Bazel structures (depset and path fragment). +type aqueryArtifactHandler struct { + // Maps middleman artifact Id to input artifact depset ID. + // Middleman artifacts are treated as "substitute" artifacts for mixed builds. For example, + // if we find a middleman action which has outputs [foo, bar], and output [baz_middleman], then, + // for each other action which has input [baz_middleman], we add [foo, bar] to the inputs for + // that action instead. + middlemanIdToDepsetIds map[int][]int + // Maps depset Id to depset struct. + depsetIdToDepset map[int]depSetOfFiles + // depsetIdToArtifactIdsCache is a memoization of depset flattening, because flattening + // may be an expensive operation. + depsetIdToArtifactIdsCache map[int][]int + // Maps artifact Id to fully expanded path. + artifactIdToPath map[int]string +} +func newAqueryHandler(aqueryResult actionGraphContainer) (*aqueryArtifactHandler, error) { pathFragments := map[int]pathFragment{} for _, pathFragment := range aqueryResult.PathFragments { pathFragments[pathFragment.Id] = pathFragment } + artifactIdToPath := map[int]string{} for _, artifact := range aqueryResult.Artifacts { artifactPath, err := expandPathFragment(artifact.PathFragmentId, pathFragments) @@ -112,22 +119,87 @@ func AqueryBuildStatements(aqueryJsonProto []byte) ([]BuildStatement, error) { depsetIdToDepset[depset.Id] = depset } - // depsetIdToArtifactIdsCache is a memoization of depset flattening, because flattening - // may be an expensive operation. - depsetIdToArtifactIdsCache := map[int][]int{} - // Do a pass through all actions to identify which artifacts are middleman artifacts. - // These will be omitted from the inputs of other actions. - // TODO(b/180945500): Handle middleman actions; without proper handling, depending on generated - // headers may cause build failures. - middlemanArtifactIds := map[int]bool{} + middlemanIdToDepsetIds := map[int][]int{} for _, actionEntry := range aqueryResult.Actions { if actionEntry.Mnemonic == "Middleman" { for _, outputId := range actionEntry.OutputIds { - middlemanArtifactIds[outputId] = true + middlemanIdToDepsetIds[outputId] = actionEntry.InputDepSetIds + } + } + } + return &aqueryArtifactHandler{ + middlemanIdToDepsetIds: middlemanIdToDepsetIds, + depsetIdToDepset: depsetIdToDepset, + depsetIdToArtifactIdsCache: map[int][]int{}, + artifactIdToPath: artifactIdToPath, + }, nil +} + +func (a *aqueryArtifactHandler) getInputPaths(depsetIds []int) ([]string, error) { + inputPaths := []string{} + + for _, inputDepSetId := range depsetIds { + inputArtifacts, err := a.artifactIdsFromDepsetId(inputDepSetId) + if err != nil { + return nil, err + } + for _, inputId := range inputArtifacts { + if middlemanInputDepsetIds, isMiddlemanArtifact := a.middlemanIdToDepsetIds[inputId]; isMiddlemanArtifact { + // Add all inputs from middleman actions which created middleman artifacts which are + // in the inputs for this action. + swappedInputPaths, err := a.getInputPaths(middlemanInputDepsetIds) + if err != nil { + return nil, err + } + inputPaths = append(inputPaths, swappedInputPaths...) + } else { + inputPath, exists := a.artifactIdToPath[inputId] + if !exists { + return nil, fmt.Errorf("undefined input artifactId %d", inputId) + } + inputPaths = append(inputPaths, inputPath) } } } + return inputPaths, nil +} + +func (a *aqueryArtifactHandler) artifactIdsFromDepsetId(depsetId int) ([]int, error) { + if result, exists := a.depsetIdToArtifactIdsCache[depsetId]; exists { + return result, nil + } + if depset, exists := a.depsetIdToDepset[depsetId]; exists { + result := depset.DirectArtifactIds + for _, childId := range depset.TransitiveDepSetIds { + childArtifactIds, err := a.artifactIdsFromDepsetId(childId) + if err != nil { + return nil, err + } + result = append(result, childArtifactIds...) + } + a.depsetIdToArtifactIdsCache[depsetId] = result + return result, nil + } else { + return nil, fmt.Errorf("undefined input depsetId %d", depsetId) + } +} + +// AqueryBuildStatements returns an array of BuildStatements which should be registered (and output +// to a ninja file) to correspond one-to-one with the given action graph json proto (from a bazel +// aquery invocation). +func AqueryBuildStatements(aqueryJsonProto []byte) ([]BuildStatement, error) { + buildStatements := []BuildStatement{} + + var aqueryResult actionGraphContainer + err := json.Unmarshal(aqueryJsonProto, &aqueryResult) + if err != nil { + return nil, err + } + aqueryHandler, err := newAqueryHandler(aqueryResult) + if err != nil { + return nil, err + } for _, actionEntry := range aqueryResult.Actions { if shouldSkipAction(actionEntry) { @@ -136,7 +208,7 @@ func AqueryBuildStatements(aqueryJsonProto []byte) ([]BuildStatement, error) { outputPaths := []string{} var depfile *string for _, outputId := range actionEntry.OutputIds { - outputPath, exists := artifactIdToPath[outputId] + outputPath, exists := aqueryHandler.artifactIdToPath[outputId] if !exists { return nil, fmt.Errorf("undefined outputId %d", outputId) } @@ -151,25 +223,11 @@ func AqueryBuildStatements(aqueryJsonProto []byte) ([]BuildStatement, error) { outputPaths = append(outputPaths, outputPath) } } - inputPaths := []string{} - for _, inputDepSetId := range actionEntry.InputDepSetIds { - inputArtifacts, err := - artifactIdsFromDepsetId(depsetIdToDepset, depsetIdToArtifactIdsCache, inputDepSetId) - if err != nil { - return nil, err - } - for _, inputId := range inputArtifacts { - if _, isMiddlemanArtifact := middlemanArtifactIds[inputId]; isMiddlemanArtifact { - // Omit middleman artifacts. - continue - } - inputPath, exists := artifactIdToPath[inputId] - if !exists { - return nil, fmt.Errorf("undefined input artifactId %d", inputId) - } - inputPaths = append(inputPaths, inputPath) - } + inputPaths, err := aqueryHandler.getInputPaths(actionEntry.InputDepSetIds) + if err != nil { + return nil, err } + buildStatement := BuildStatement{ Command: strings.Join(proptools.ShellEscapeList(actionEntry.Arguments), " "), Depfile: depfile, @@ -192,8 +250,9 @@ func shouldSkipAction(a action) bool { if a.Mnemonic == "Symlink" || a.Mnemonic == "SourceSymlinkManifest" || a.Mnemonic == "SymlinkTree" { return true } - // TODO(b/180945500): Handle middleman actions; without proper handling, depending on generated - // headers may cause build failures. + // Middleman actions are not handled like other actions; they are handled separately as a + // preparatory step so that their inputs may be relayed to actions depending on middleman + // artifacts. if a.Mnemonic == "Middleman" { return true } @@ -209,28 +268,6 @@ func shouldSkipAction(a action) bool { return false } -func artifactIdsFromDepsetId(depsetIdToDepset map[int]depSetOfFiles, - depsetIdToArtifactIdsCache map[int][]int, depsetId int) ([]int, error) { - if result, exists := depsetIdToArtifactIdsCache[depsetId]; exists { - return result, nil - } - if depset, exists := depsetIdToDepset[depsetId]; exists { - result := depset.DirectArtifactIds - for _, childId := range depset.TransitiveDepSetIds { - childArtifactIds, err := - artifactIdsFromDepsetId(depsetIdToDepset, depsetIdToArtifactIdsCache, childId) - if err != nil { - return nil, err - } - result = append(result, childArtifactIds...) - } - depsetIdToArtifactIdsCache[depsetId] = result - return result, nil - } else { - return nil, fmt.Errorf("undefined input depsetId %d", depsetId) - } -} - func expandPathFragment(id int, pathFragmentsMap map[int]pathFragment) (string, error) { labels := []string{} currId := id diff --git a/bazel/aquery_test.go b/bazel/aquery_test.go index fa8810f0d..7b40dcdda 100644 --- a/bazel/aquery_test.go +++ b/bazel/aquery_test.go @@ -718,6 +718,93 @@ func TestTransitiveInputDepsets(t *testing.T) { assertBuildStatements(t, expectedBuildStatements, actualbuildStatements) } +func TestMiddlemenAction(t *testing.T) { + const inputString = ` +{ + "artifacts": [{ + "id": 1, + "pathFragmentId": 1 + }, { + "id": 2, + "pathFragmentId": 2 + }, { + "id": 3, + "pathFragmentId": 3 + }, { + "id": 4, + "pathFragmentId": 4 + }, { + "id": 5, + "pathFragmentId": 5 + }, { + "id": 6, + "pathFragmentId": 6 + }], + "pathFragments": [{ + "id": 1, + "label": "middleinput_one" + }, { + "id": 2, + "label": "middleinput_two" + }, { + "id": 3, + "label": "middleman_artifact" + }, { + "id": 4, + "label": "maininput_one" + }, { + "id": 5, + "label": "maininput_two" + }, { + "id": 6, + "label": "output" + }], + "depSetOfFiles": [{ + "id": 1, + "directArtifactIds": [1, 2] + }, { + "id": 2, + "directArtifactIds": [3, 4, 5] + }], + "actions": [{ + "targetId": 1, + "actionKey": "x", + "mnemonic": "Middleman", + "arguments": ["touch", "foo"], + "inputDepSetIds": [1], + "outputIds": [3], + "primaryOutputId": 3 + }, { + "targetId": 2, + "actionKey": "y", + "mnemonic": "Main action", + "arguments": ["touch", "foo"], + "inputDepSetIds": [2], + "outputIds": [6], + "primaryOutputId": 6 + }] +}` + + actual, err := AqueryBuildStatements([]byte(inputString)) + if err != nil { + t.Errorf("Unexpected error %q", err) + } + if expected := 1; len(actual) != expected { + t.Fatalf("Expected %d build statements, got %d", expected, len(actual)) + } + + bs := actual[0] + expectedInputs := []string{"middleinput_one", "middleinput_two", "maininput_one", "maininput_two"} + if !reflect.DeepEqual(bs.InputPaths, expectedInputs) { + t.Errorf("Expected main action inputs %q, but got %q", expectedInputs, bs.InputPaths) + } + + expectedOutputs := []string{"output"} + if !reflect.DeepEqual(bs.OutputPaths, expectedOutputs) { + t.Errorf("Expected main action outputs %q, but got %q", expectedOutputs, bs.OutputPaths) + } +} + func assertError(t *testing.T, err error, expected string) { if err == nil { t.Errorf("expected error '%s', but got no error", expected) diff --git a/bazel/constants.go b/bazel/constants.go index 15c75cf4a..6beb496a5 100644 --- a/bazel/constants.go +++ b/bazel/constants.go @@ -18,6 +18,10 @@ const ( // Run bazel as a ninja executer BazelNinjaExecRunName = RunName("bazel-ninja-exec") + + SoongInjectionDirName = "soong_injection" + + GeneratedBazelFileWarning = "# GENERATED FOR BAZEL FROM SOONG. DO NOT EDIT" ) // String returns the name of the run. diff --git a/bazel/properties.go b/bazel/properties.go index a71b12bfd..3e778bb69 100644 --- a/bazel/properties.go +++ b/bazel/properties.go @@ -160,6 +160,14 @@ func SubtractBazelLabels(haystack []Label, needle []Label) []Label { return labels } +// Appends two LabelLists, returning the combined list. +func AppendBazelLabelLists(a LabelList, b LabelList) LabelList { + var result LabelList + result.Includes = append(a.Includes, b.Includes...) + result.Excludes = append(a.Excludes, b.Excludes...) + return result +} + // Subtract needle from haystack func SubtractBazelLabelList(haystack LabelList, needle LabelList) LabelList { var result LabelList diff --git a/bp2build/Android.bp b/bp2build/Android.bp index abd79f56f..3abbc4c88 100644 --- a/bp2build/Android.bp +++ b/bp2build/Android.bp @@ -20,6 +20,7 @@ bootstrap_go_package { "soong-android", "soong-bazel", "soong-cc", + "soong-cc-config", "soong-genrule", "soong-python", "soong-sh", diff --git a/bp2build/bp2build.go b/bp2build/bp2build.go index cf6994fd9..ee36982c7 100644 --- a/bp2build/bp2build.go +++ b/bp2build/bp2build.go @@ -16,6 +16,7 @@ package bp2build import ( "android/soong/android" + "android/soong/bazel" "fmt" "os" ) @@ -24,22 +25,16 @@ import ( // writing .bzl files that are equivalent to Android.bp files that are capable // of being built with Bazel. func Codegen(ctx *CodegenContext) CodegenMetrics { - outputDir := android.PathForOutput(ctx, "bp2build") - android.RemoveAllOutputDir(outputDir) + // This directory stores BUILD files that could be eventually checked-in. + bp2buildDir := android.PathForOutput(ctx, "bp2build") + android.RemoveAllOutputDir(bp2buildDir) buildToTargets, metrics := GenerateBazelTargets(ctx, true) + bp2buildFiles := CreateBazelFiles(nil, buildToTargets, ctx.mode) + writeFiles(ctx, bp2buildDir, bp2buildFiles) - filesToWrite := CreateBazelFiles(nil, buildToTargets, ctx.mode) - - generatedBuildFiles := []string{} - for _, f := range filesToWrite { - p := getOrCreateOutputDir(outputDir, ctx, f.Dir).Join(ctx, f.Basename) - if err := writeFile(ctx, p, f.Contents); err != nil { - panic(fmt.Errorf("Failed to write %q (dir %q) due to %q", f.Basename, f.Dir, err)) - } - // if these generated files are modified, regenerate on next run. - generatedBuildFiles = append(generatedBuildFiles, p.String()) - } + soongInjectionDir := android.PathForOutput(ctx, bazel.SoongInjectionDirName) + writeFiles(ctx, soongInjectionDir, CreateSoongInjectionFiles()) return metrics } @@ -51,6 +46,16 @@ func getOrCreateOutputDir(outputDir android.OutputPath, ctx android.PathContext, return dirPath } +// writeFiles materializes a list of BazelFile rooted at outputDir. +func writeFiles(ctx android.PathContext, outputDir android.OutputPath, files []BazelFile) { + for _, f := range files { + p := getOrCreateOutputDir(outputDir, ctx, f.Dir).Join(ctx, f.Basename) + if err := writeFile(ctx, p, f.Contents); err != nil { + panic(fmt.Errorf("Failed to write %q (dir %q) due to %q", f.Basename, f.Dir, err)) + } + } +} + func writeFile(ctx android.PathContext, pathToFile android.OutputPath, content string) error { // These files are made editable to allow users to modify and iterate on them // in the source tree. diff --git a/bp2build/build_conversion_test.go b/bp2build/build_conversion_test.go index 63a6c2e5c..71660a8e1 100644 --- a/bp2build/build_conversion_test.go +++ b/bp2build/build_conversion_test.go @@ -324,11 +324,11 @@ custom { ctx.RegisterForBazelConversion() _, errs := ctx.ParseFileList(dir, []string{"Android.bp"}) - if Errored(t, "", errs) { + if errored(t, "", errs) { continue } _, errs = ctx.ResolveDependencies(config) - if Errored(t, "", errs) { + if errored(t, "", errs) { continue } @@ -925,11 +925,11 @@ genrule { ctx.RegisterForBazelConversion() _, errs := ctx.ParseFileList(dir, toParse) - if Errored(t, testCase.description, errs) { + if errored(t, testCase.description, errs) { continue } _, errs = ctx.ResolveDependencies(config) - if Errored(t, testCase.description, errs) { + if errored(t, testCase.description, errs) { continue } @@ -957,17 +957,6 @@ genrule { } } -func Errored(t *testing.T, desc string, errs []error) bool { - t.Helper() - if len(errs) > 0 { - for _, err := range errs { - t.Errorf("%s: %s", desc, err) - } - return true - } - return false -} - type bp2buildMutator = func(android.TopDownMutatorContext) func TestBp2BuildInlinesDefaults(t *testing.T) { @@ -1483,11 +1472,11 @@ filegroup { ctx.RegisterForBazelConversion() _, errs := ctx.ParseFileList(dir, toParse) - if Errored(t, testCase.description, errs) { + if errored(t, testCase.description, errs) { continue } _, errs = ctx.ResolveDependencies(config) - if Errored(t, testCase.description, errs) { + if errored(t, testCase.description, errs) { continue } @@ -1606,11 +1595,11 @@ func TestGlobExcludeSrcs(t *testing.T) { ctx.RegisterForBazelConversion() _, errs := ctx.ParseFileList(dir, toParse) - if Errored(t, testCase.description, errs) { + if errored(t, testCase.description, errs) { continue } _, errs = ctx.ResolveDependencies(config) - if Errored(t, testCase.description, errs) { + if errored(t, testCase.description, errs) { continue } diff --git a/bp2build/bzl_conversion_test.go b/bp2build/bzl_conversion_test.go index 32b12e42e..204c51986 100644 --- a/bp2build/bzl_conversion_test.go +++ b/bp2build/bzl_conversion_test.go @@ -22,8 +22,6 @@ import ( "testing" ) -var buildDir string - func setUp() { var err error buildDir, err = ioutil.TempDir("", "bazel_queryview_test") diff --git a/bp2build/cc_library_conversion_test.go b/bp2build/cc_library_conversion_test.go index 8f060c011..0a729374e 100644 --- a/bp2build/cc_library_conversion_test.go +++ b/bp2build/cc_library_conversion_test.go @@ -40,43 +40,98 @@ toolchain_library { }` ) -func TestCcLibraryBp2Build(t *testing.T) { - testCases := []struct { - description string - moduleTypeUnderTest string - moduleTypeUnderTestFactory android.ModuleFactory - moduleTypeUnderTestBp2BuildMutator func(android.TopDownMutatorContext) - bp string - expectedBazelTargets []string - filesystem map[string]string - dir string - depsMutators []android.RegisterMutatorFunc - }{ - { - description: "cc_library - simple example", - moduleTypeUnderTest: "cc_library", - moduleTypeUnderTestFactory: cc.LibraryFactory, - moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build, - filesystem: map[string]string{ - "android.cpp": "", - "darwin.cpp": "", - // Refer to cc.headerExts for the supported header extensions in Soong. - "header.h": "", - "header.hh": "", - "header.hpp": "", - "header.hxx": "", - "header.h++": "", - "header.inl": "", - "header.inc": "", - "header.ipp": "", - "header.h.generic": "", - "impl.cpp": "", - "linux.cpp": "", - "x86.cpp": "", - "x86_64.cpp": "", - "foo-dir/a.h": "", - }, - bp: soongCcLibraryPreamble + ` +func runCcLibraryTestCase(t *testing.T, tc bp2buildTestCase) { + runBp2BuildTestCase(t, registerCcLibraryModuleTypes, tc) +} + +func registerCcLibraryModuleTypes(ctx android.RegistrationContext) { + cc.RegisterCCBuildComponents(ctx) + ctx.RegisterModuleType("cc_library_static", cc.LibraryStaticFactory) + ctx.RegisterModuleType("toolchain_library", cc.ToolchainLibraryFactory) + ctx.RegisterModuleType("cc_library_headers", cc.LibraryHeaderFactory) +} + +func runBp2BuildTestCase(t *testing.T, registerModuleTypes func(ctx android.RegistrationContext), tc bp2buildTestCase) { + dir := "." + filesystem := make(map[string][]byte) + toParse := []string{ + "Android.bp", + } + for f, content := range tc.filesystem { + if strings.HasSuffix(f, "Android.bp") { + toParse = append(toParse, f) + } + filesystem[f] = []byte(content) + } + config := android.TestConfig(buildDir, nil, tc.blueprint, filesystem) + ctx := android.NewTestContext(config) + + registerModuleTypes(ctx) + ctx.RegisterModuleType(tc.moduleTypeUnderTest, tc.moduleTypeUnderTestFactory) + ctx.RegisterBp2BuildConfig(bp2buildConfig) + for _, m := range tc.depsMutators { + ctx.DepsBp2BuildMutators(m) + } + ctx.RegisterBp2BuildMutator(tc.moduleTypeUnderTest, tc.moduleTypeUnderTestBp2BuildMutator) + ctx.RegisterForBazelConversion() + + _, errs := ctx.ParseFileList(dir, toParse) + if errored(t, tc.description, errs) { + return + } + _, errs = ctx.ResolveDependencies(config) + if errored(t, tc.description, errs) { + return + } + + checkDir := dir + if tc.dir != "" { + checkDir = tc.dir + } + codegenCtx := NewCodegenContext(config, *ctx.Context, Bp2Build) + bazelTargets := generateBazelTargetsForDir(codegenCtx, checkDir) + if actualCount, expectedCount := len(bazelTargets), len(tc.expectedBazelTargets); actualCount != expectedCount { + t.Errorf("%s: Expected %d bazel target, got %d", tc.description, expectedCount, actualCount) + } else { + for i, target := range bazelTargets { + if w, g := tc.expectedBazelTargets[i], target.content; w != g { + t.Errorf( + "%s: Expected generated Bazel target to be '%s', got '%s'", + tc.description, + w, + g, + ) + } + } + } +} + +func TestCcLibrarySimple(t *testing.T) { + runCcLibraryTestCase(t, bp2buildTestCase{ + description: "cc_library - simple example", + moduleTypeUnderTest: "cc_library", + moduleTypeUnderTestFactory: cc.LibraryFactory, + moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build, + filesystem: map[string]string{ + "android.cpp": "", + "darwin.cpp": "", + // Refer to cc.headerExts for the supported header extensions in Soong. + "header.h": "", + "header.hh": "", + "header.hpp": "", + "header.hxx": "", + "header.h++": "", + "header.inl": "", + "header.inc": "", + "header.ipp": "", + "header.h.generic": "", + "impl.cpp": "", + "linux.cpp": "", + "x86.cpp": "", + "x86_64.cpp": "", + "foo-dir/a.h": "", + }, + blueprint: soongCcLibraryPreamble + ` cc_library_headers { name: "some-headers" } cc_library { name: "foo-lib", @@ -108,13 +163,14 @@ cc_library { }, } `, - expectedBazelTargets: []string{`cc_library( + expectedBazelTargets: []string{`cc_library( name = "foo-lib", copts = [ "-Wall", "-I.", + "-I$(BINDIR)/.", ], - deps = [":some-headers"], + implementation_deps = [":some-headers"], includes = ["foo-dir"], linkopts = ["-Wl,--exclude-libs=bar.a"] + select({ "//build/bazel/platforms/arch:x86": ["-Wl,--exclude-libs=baz.a"], @@ -131,21 +187,23 @@ cc_library { "//build/bazel/platforms/os:linux": ["linux.cpp"], "//conditions:default": [], }), -)`}, +)`}}) +} + +func TestCcLibraryTrimmedLdAndroid(t *testing.T) { + runCcLibraryTestCase(t, bp2buildTestCase{ + description: "cc_library - trimmed example of //bionic/linker:ld-android", + moduleTypeUnderTest: "cc_library", + moduleTypeUnderTestFactory: cc.LibraryFactory, + moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build, + filesystem: map[string]string{ + "ld-android.cpp": "", + "linked_list.h": "", + "linker.h": "", + "linker_block_allocator.h": "", + "linker_cfi.h": "", }, - { - description: "cc_library - trimmed example of //bionic/linker:ld-android", - moduleTypeUnderTest: "cc_library", - moduleTypeUnderTestFactory: cc.LibraryFactory, - moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build, - filesystem: map[string]string{ - "ld-android.cpp": "", - "linked_list.h": "", - "linker.h": "", - "linker_block_allocator.h": "", - "linker_cfi.h": "", - }, - bp: soongCcLibraryPreamble + ` + blueprint: soongCcLibraryPreamble + ` cc_library_headers { name: "libc_headers" } cc_library { name: "fake-ld-android", @@ -175,7 +233,7 @@ cc_library { }, } `, - expectedBazelTargets: []string{`cc_library( + expectedBazelTargets: []string{`cc_library( name = "fake-ld-android", copts = [ "-Wall", @@ -183,8 +241,9 @@ cc_library { "-Wunused", "-Werror", "-I.", + "-I$(BINDIR)/.", ], - deps = [":libc_headers"], + implementation_deps = [":libc_headers"], linkopts = [ "-Wl,--exclude-libs=libgcc.a", "-Wl,--exclude-libs=libgcc_stripped.a", @@ -199,20 +258,23 @@ cc_library { }), srcs = ["ld_android.cpp"], )`}, - }, - { - description: "cc_library exclude_srcs - trimmed example of //external/arm-optimized-routines:libarm-optimized-routines-math", - moduleTypeUnderTest: "cc_library", - moduleTypeUnderTestFactory: cc.LibraryFactory, - moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build, - dir: "external", - filesystem: map[string]string{ - "external/math/cosf.c": "", - "external/math/erf.c": "", - "external/math/erf_data.c": "", - "external/math/erff.c": "", - "external/math/erff_data.c": "", - "external/Android.bp": ` + }) +} + +func TestCcLibraryExcludeSrcs(t *testing.T) { + runCcLibraryTestCase(t, bp2buildTestCase{ + description: "cc_library exclude_srcs - trimmed example of //external/arm-optimized-routines:libarm-optimized-routines-math", + moduleTypeUnderTest: "cc_library", + moduleTypeUnderTestFactory: cc.LibraryFactory, + moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build, + dir: "external", + filesystem: map[string]string{ + "external/math/cosf.c": "", + "external/math/erf.c": "", + "external/math/erf_data.c": "", + "external/math/erff.c": "", + "external/math/erff_data.c": "", + "external/Android.bp": ` cc_library { name: "fake-libarm-optimized-routines-math", exclude_srcs: [ @@ -238,29 +300,35 @@ cc_library { bazel_module: { bp2build_available: true }, } `, - }, - bp: soongCcLibraryPreamble, - expectedBazelTargets: []string{`cc_library( + }, + blueprint: soongCcLibraryPreamble, + expectedBazelTargets: []string{`cc_library( name = "fake-libarm-optimized-routines-math", - copts = ["-Iexternal"] + select({ + copts = [ + "-Iexternal", + "-I$(BINDIR)/external", + ] + select({ "//build/bazel/platforms/arch:arm64": ["-DHAVE_FAST_FMA=1"], "//conditions:default": [], }), srcs = ["math/cosf.c"], )`}, - }, - { - description: "cc_library shared/static props", - moduleTypeUnderTest: "cc_library", - moduleTypeUnderTestFactory: cc.LibraryFactory, - moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build, - depsMutators: []android.RegisterMutatorFunc{cc.RegisterDepsBp2Build}, - dir: "foo/bar", - filesystem: map[string]string{ - "foo/bar/both.cpp": "", - "foo/bar/sharedonly.cpp": "", - "foo/bar/staticonly.cpp": "", - "foo/bar/Android.bp": ` + }) +} + +func TestCcLibrarySharedStaticProps(t *testing.T) { + runCcLibraryTestCase(t, bp2buildTestCase{ + description: "cc_library shared/static props", + moduleTypeUnderTest: "cc_library", + moduleTypeUnderTestFactory: cc.LibraryFactory, + moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build, + depsMutators: []android.RegisterMutatorFunc{cc.RegisterDepsBp2Build}, + dir: "foo/bar", + filesystem: map[string]string{ + "foo/bar/both.cpp": "", + "foo/bar/sharedonly.cpp": "", + "foo/bar/staticonly.cpp": "", + "foo/bar/Android.bp": ` cc_library { name: "a", srcs: ["both.cpp"], @@ -303,18 +371,19 @@ cc_library { name: "shared_dep_for_static" } cc_library { name: "shared_dep_for_both" } `, - }, - bp: soongCcLibraryPreamble, - expectedBazelTargets: []string{`cc_library( + }, + blueprint: soongCcLibraryPreamble, + expectedBazelTargets: []string{`cc_library( name = "a", copts = [ "bothflag", "-Ifoo/bar", + "-I$(BINDIR)/foo/bar", ], - deps = [":static_dep_for_both"], dynamic_deps = [":shared_dep_for_both"], dynamic_deps_for_shared = [":shared_dep_for_shared"], dynamic_deps_for_static = [":shared_dep_for_static"], + implementation_deps = [":static_dep_for_both"], shared_copts = ["sharedflag"], shared_srcs = ["sharedonly.cpp"], srcs = ["both.cpp"], @@ -326,16 +395,19 @@ cc_library { name: "shared_dep_for_both" } whole_archive_deps_for_shared = [":whole_static_lib_for_shared"], whole_archive_deps_for_static = [":whole_static_lib_for_static"], )`}, - }, - { - description: "cc_library non-configured version script", - moduleTypeUnderTest: "cc_library", - moduleTypeUnderTestFactory: cc.LibraryFactory, - moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build, - depsMutators: []android.RegisterMutatorFunc{cc.RegisterDepsBp2Build}, - dir: "foo/bar", - filesystem: map[string]string{ - "foo/bar/Android.bp": ` + }) +} + +func TestCcLibraryNonConfiguredVersionScript(t *testing.T) { + runCcLibraryTestCase(t, bp2buildTestCase{ + description: "cc_library non-configured version script", + moduleTypeUnderTest: "cc_library", + moduleTypeUnderTestFactory: cc.LibraryFactory, + moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build, + depsMutators: []android.RegisterMutatorFunc{cc.RegisterDepsBp2Build}, + dir: "foo/bar", + filesystem: map[string]string{ + "foo/bar/Android.bp": ` cc_library { name: "a", srcs: ["a.cpp"], @@ -343,24 +415,73 @@ cc_library { bazel_module: { bp2build_available: true }, } `, - }, - bp: soongCcLibraryPreamble, - expectedBazelTargets: []string{`cc_library( + }, + blueprint: soongCcLibraryPreamble, + expectedBazelTargets: []string{`cc_library( name = "a", - copts = ["-Ifoo/bar"], + copts = [ + "-Ifoo/bar", + "-I$(BINDIR)/foo/bar", + ], srcs = ["a.cpp"], version_script = "v.map", )`}, + }) +} + +func TestCcLibraryConfiguredVersionScript(t *testing.T) { + runCcLibraryTestCase(t, bp2buildTestCase{ + description: "cc_library configured version script", + moduleTypeUnderTest: "cc_library", + moduleTypeUnderTestFactory: cc.LibraryFactory, + moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build, + depsMutators: []android.RegisterMutatorFunc{cc.RegisterDepsBp2Build}, + dir: "foo/bar", + filesystem: map[string]string{ + "foo/bar/Android.bp": ` + cc_library { + name: "a", + srcs: ["a.cpp"], + arch: { + arm: { + version_script: "arm.map", + }, + arm64: { + version_script: "arm64.map", + }, + }, + + bazel_module: { bp2build_available: true }, + } + `, }, - { - description: "cc_library shared_libs", - moduleTypeUnderTest: "cc_library", - moduleTypeUnderTestFactory: cc.LibraryFactory, - moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build, - depsMutators: []android.RegisterMutatorFunc{cc.RegisterDepsBp2Build}, - dir: "foo/bar", - filesystem: map[string]string{ - "foo/bar/Android.bp": ` + blueprint: soongCcLibraryPreamble, + expectedBazelTargets: []string{`cc_library( + name = "a", + copts = [ + "-Ifoo/bar", + "-I$(BINDIR)/foo/bar", + ], + srcs = ["a.cpp"], + version_script = select({ + "//build/bazel/platforms/arch:arm": "arm.map", + "//build/bazel/platforms/arch:arm64": "arm64.map", + "//conditions:default": None, + }), +)`}, + }) +} + +func TestCcLibrarySharedLibs(t *testing.T) { + runCcLibraryTestCase(t, bp2buildTestCase{ + description: "cc_library shared_libs", + moduleTypeUnderTest: "cc_library", + moduleTypeUnderTestFactory: cc.LibraryFactory, + moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build, + depsMutators: []android.RegisterMutatorFunc{cc.RegisterDepsBp2Build}, + dir: "foo/bar", + filesystem: map[string]string{ + "foo/bar/Android.bp": ` cc_library { name: "mylib", bazel_module: { bp2build_available: true }, @@ -372,26 +493,35 @@ cc_library { bazel_module: { bp2build_available: true }, } `, - }, - bp: soongCcLibraryPreamble, - expectedBazelTargets: []string{`cc_library( + }, + blueprint: soongCcLibraryPreamble, + expectedBazelTargets: []string{`cc_library( name = "a", - copts = ["-Ifoo/bar"], + copts = [ + "-Ifoo/bar", + "-I$(BINDIR)/foo/bar", + ], dynamic_deps = [":mylib"], )`, `cc_library( name = "mylib", - copts = ["-Ifoo/bar"], + copts = [ + "-Ifoo/bar", + "-I$(BINDIR)/foo/bar", + ], )`}, - }, - { - description: "cc_library pack_relocations test", - moduleTypeUnderTest: "cc_library", - moduleTypeUnderTestFactory: cc.LibraryFactory, - moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build, - depsMutators: []android.RegisterMutatorFunc{cc.RegisterDepsBp2Build}, - dir: "foo/bar", - filesystem: map[string]string{ - "foo/bar/Android.bp": ` + }) +} + +func TestCcLibraryPackRelocations(t *testing.T) { + runCcLibraryTestCase(t, bp2buildTestCase{ + description: "cc_library pack_relocations test", + moduleTypeUnderTest: "cc_library", + moduleTypeUnderTestFactory: cc.LibraryFactory, + moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build, + depsMutators: []android.RegisterMutatorFunc{cc.RegisterDepsBp2Build}, + dir: "foo/bar", + filesystem: map[string]string{ + "foo/bar/Android.bp": ` cc_library { name: "a", srcs: ["a.cpp"], @@ -420,16 +550,22 @@ cc_library { }, bazel_module: { bp2build_available: true }, }`, - }, - bp: soongCcLibraryPreamble, - expectedBazelTargets: []string{`cc_library( + }, + blueprint: soongCcLibraryPreamble, + expectedBazelTargets: []string{`cc_library( name = "a", - copts = ["-Ifoo/bar"], + copts = [ + "-Ifoo/bar", + "-I$(BINDIR)/foo/bar", + ], linkopts = ["-Wl,--pack-dyn-relocs=none"], srcs = ["a.cpp"], )`, `cc_library( name = "b", - copts = ["-Ifoo/bar"], + copts = [ + "-Ifoo/bar", + "-I$(BINDIR)/foo/bar", + ], linkopts = select({ "//build/bazel/platforms/arch:x86_64": ["-Wl,--pack-dyn-relocs=none"], "//conditions:default": [], @@ -437,97 +573,99 @@ cc_library { srcs = ["b.cpp"], )`, `cc_library( name = "c", - copts = ["-Ifoo/bar"], + copts = [ + "-Ifoo/bar", + "-I$(BINDIR)/foo/bar", + ], linkopts = select({ "//build/bazel/platforms/os:darwin": ["-Wl,--pack-dyn-relocs=none"], "//conditions:default": [], }), srcs = ["c.cpp"], )`}, - }, - { - description: "cc_library spaces in copts", - moduleTypeUnderTest: "cc_library", - moduleTypeUnderTestFactory: cc.LibraryFactory, - moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build, - depsMutators: []android.RegisterMutatorFunc{cc.RegisterDepsBp2Build}, - dir: "foo/bar", - filesystem: map[string]string{ - "foo/bar/Android.bp": ` + }) +} + +func TestCcLibrarySpacesInCopts(t *testing.T) { + runCcLibraryTestCase(t, bp2buildTestCase{ + description: "cc_library spaces in copts", + moduleTypeUnderTest: "cc_library", + moduleTypeUnderTestFactory: cc.LibraryFactory, + moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build, + depsMutators: []android.RegisterMutatorFunc{cc.RegisterDepsBp2Build}, + dir: "foo/bar", + filesystem: map[string]string{ + "foo/bar/Android.bp": ` cc_library { name: "a", cflags: ["-include header.h",], bazel_module: { bp2build_available: true }, } `, - }, - bp: soongCcLibraryPreamble, - expectedBazelTargets: []string{`cc_library( + }, + blueprint: soongCcLibraryPreamble, + expectedBazelTargets: []string{`cc_library( name = "a", copts = [ "-include", "header.h", "-Ifoo/bar", + "-I$(BINDIR)/foo/bar", ], )`}, - }, - } - - dir := "." - for _, testCase := range testCases { - filesystem := make(map[string][]byte) - toParse := []string{ - "Android.bp", - } - for f, content := range testCase.filesystem { - if strings.HasSuffix(f, "Android.bp") { - toParse = append(toParse, f) - } - filesystem[f] = []byte(content) - } - config := android.TestConfig(buildDir, nil, testCase.bp, filesystem) - ctx := android.NewTestContext(config) - - cc.RegisterCCBuildComponents(ctx) - ctx.RegisterModuleType("cc_library_static", cc.LibraryStaticFactory) - ctx.RegisterModuleType("toolchain_library", cc.ToolchainLibraryFactory) - ctx.RegisterModuleType("cc_library_headers", cc.LibraryHeaderFactory) - ctx.RegisterModuleType(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestFactory) - ctx.RegisterBp2BuildMutator(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestBp2BuildMutator) - ctx.RegisterBp2BuildConfig(bp2buildConfig) // TODO(jingwen): make this the default for all tests - for _, m := range testCase.depsMutators { - ctx.DepsBp2BuildMutators(m) - } - ctx.RegisterForBazelConversion() - - _, errs := ctx.ParseFileList(dir, toParse) - if Errored(t, testCase.description, errs) { - continue - } - _, errs = ctx.ResolveDependencies(config) - if Errored(t, testCase.description, errs) { - continue - } + }) +} - checkDir := dir - if testCase.dir != "" { - checkDir = testCase.dir - } - codegenCtx := NewCodegenContext(config, *ctx.Context, Bp2Build) - bazelTargets := generateBazelTargetsForDir(codegenCtx, checkDir) - if actualCount, expectedCount := len(bazelTargets), len(testCase.expectedBazelTargets); actualCount != expectedCount { - t.Errorf("%s: Expected %d bazel target, got %d", testCase.description, expectedCount, actualCount) - } else { - for i, target := range bazelTargets { - if w, g := testCase.expectedBazelTargets[i], target.content; w != g { - t.Errorf( - "%s: Expected generated Bazel target to be '%s', got '%s'", - testCase.description, - w, - g, - ) - } - } - } - } +func TestCcLibraryCppFlagsGoesIntoCopts(t *testing.T) { + runCcLibraryTestCase(t, bp2buildTestCase{ + description: "cc_library cppflags goes into copts", + moduleTypeUnderTest: "cc_library", + moduleTypeUnderTestFactory: cc.LibraryFactory, + moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build, + depsMutators: []android.RegisterMutatorFunc{cc.RegisterDepsBp2Build}, + dir: "foo/bar", + filesystem: map[string]string{ + "foo/bar/Android.bp": `cc_library { + name: "a", + srcs: ["a.cpp"], + cflags: [ + "-Wall", + ], + cppflags: [ + "-fsigned-char", + "-pedantic", + ], + arch: { + arm64: { + cppflags: ["-DARM64=1"], + }, + }, + target: { + android: { + cppflags: ["-DANDROID=1"], + }, + }, + bazel_module: { bp2build_available: true }, +} +`, + }, + blueprint: soongCcLibraryPreamble, + expectedBazelTargets: []string{`cc_library( + name = "a", + copts = [ + "-Wall", + "-fsigned-char", + "-pedantic", + "-Ifoo/bar", + "-I$(BINDIR)/foo/bar", + ] + select({ + "//build/bazel/platforms/arch:arm64": ["-DARM64=1"], + "//conditions:default": [], + }) + select({ + "//build/bazel/platforms/os:android": ["-DANDROID=1"], + "//conditions:default": [], + }), + srcs = ["a.cpp"], +)`}, + }) } diff --git a/bp2build/cc_library_headers_conversion_test.go b/bp2build/cc_library_headers_conversion_test.go index 0905aba1f..2859bab70 100644 --- a/bp2build/cc_library_headers_conversion_test.go +++ b/bp2build/cc_library_headers_conversion_test.go @@ -15,10 +15,10 @@ package bp2build import ( + "testing" + "android/soong/android" "android/soong/cc" - "strings" - "testing" ) const ( @@ -40,6 +40,18 @@ toolchain_library { }` ) +type bp2buildTestCase struct { + description string + moduleTypeUnderTest string + moduleTypeUnderTestFactory android.ModuleFactory + moduleTypeUnderTestBp2BuildMutator func(android.TopDownMutatorContext) + depsMutators []android.RegisterMutatorFunc + blueprint string + expectedBazelTargets []string + filesystem map[string]string + dir string +} + func TestCcLibraryHeadersLoadStatement(t *testing.T) { testCases := []struct { bazelTargets BazelTargets @@ -64,41 +76,37 @@ func TestCcLibraryHeadersLoadStatement(t *testing.T) { t.Fatalf("Expected load statements to be %s, got %s", expected, actual) } } +} +func registerCcLibraryHeadersModuleTypes(ctx android.RegistrationContext) { + cc.RegisterCCBuildComponents(ctx) + ctx.RegisterModuleType("toolchain_library", cc.ToolchainLibraryFactory) } -func TestCcLibraryHeadersBp2Build(t *testing.T) { - testCases := []struct { - description string - moduleTypeUnderTest string - moduleTypeUnderTestFactory android.ModuleFactory - moduleTypeUnderTestBp2BuildMutator func(android.TopDownMutatorContext) - preArchMutators []android.RegisterMutatorFunc - depsMutators []android.RegisterMutatorFunc - bp string - expectedBazelTargets []string - filesystem map[string]string - dir string - }{ - { - description: "cc_library_headers test", - moduleTypeUnderTest: "cc_library_headers", - moduleTypeUnderTestFactory: cc.LibraryHeaderFactory, - moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryHeadersBp2Build, - filesystem: map[string]string{ - "lib-1/lib1a.h": "", - "lib-1/lib1b.h": "", - "lib-2/lib2a.h": "", - "lib-2/lib2b.h": "", - "dir-1/dir1a.h": "", - "dir-1/dir1b.h": "", - "dir-2/dir2a.h": "", - "dir-2/dir2b.h": "", - "arch_arm64_exported_include_dir/a.h": "", - "arch_x86_exported_include_dir/b.h": "", - "arch_x86_64_exported_include_dir/c.h": "", - }, - bp: soongCcLibraryHeadersPreamble + ` +func runCcLibraryHeadersTestCase(t *testing.T, tc bp2buildTestCase) { + runBp2BuildTestCase(t, registerCcLibraryHeadersModuleTypes, tc) +} + +func TestCcLibraryHeadersSimple(t *testing.T) { + runCcLibraryHeadersTestCase(t, bp2buildTestCase{ + description: "cc_library_headers test", + moduleTypeUnderTest: "cc_library_headers", + moduleTypeUnderTestFactory: cc.LibraryHeaderFactory, + moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryHeadersBp2Build, + filesystem: map[string]string{ + "lib-1/lib1a.h": "", + "lib-1/lib1b.h": "", + "lib-2/lib2a.h": "", + "lib-2/lib2b.h": "", + "dir-1/dir1a.h": "", + "dir-1/dir1b.h": "", + "dir-2/dir2a.h": "", + "dir-2/dir2b.h": "", + "arch_arm64_exported_include_dir/a.h": "", + "arch_x86_exported_include_dir/b.h": "", + "arch_x86_64_exported_include_dir/c.h": "", + }, + blueprint: soongCcLibraryHeadersPreamble + ` cc_library_headers { name: "lib-1", export_include_dirs: ["lib-1"], @@ -129,10 +137,13 @@ cc_library_headers { // TODO: Also support export_header_lib_headers }`, - expectedBazelTargets: []string{`cc_library_headers( + expectedBazelTargets: []string{`cc_library_headers( name = "foo_headers", - copts = ["-I."], - deps = [ + copts = [ + "-I.", + "-I$(BINDIR)/.", + ], + implementation_deps = [ ":lib-1", ":lib-2", ], @@ -147,22 +158,31 @@ cc_library_headers { }), )`, `cc_library_headers( name = "lib-1", - copts = ["-I."], + copts = [ + "-I.", + "-I$(BINDIR)/.", + ], includes = ["lib-1"], )`, `cc_library_headers( name = "lib-2", - copts = ["-I."], + copts = [ + "-I.", + "-I$(BINDIR)/.", + ], includes = ["lib-2"], )`}, - }, - { - description: "cc_library_headers test with os-specific header_libs props", - moduleTypeUnderTest: "cc_library_headers", - moduleTypeUnderTestFactory: cc.LibraryHeaderFactory, - moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryHeadersBp2Build, - depsMutators: []android.RegisterMutatorFunc{cc.RegisterDepsBp2Build}, - filesystem: map[string]string{}, - bp: soongCcLibraryPreamble + ` + }) +} + +func TestCcLibraryHeadersOSSpecificHeader(t *testing.T) { + runCcLibraryHeadersTestCase(t, bp2buildTestCase{ + description: "cc_library_headers test with os-specific header_libs props", + moduleTypeUnderTest: "cc_library_headers", + moduleTypeUnderTestFactory: cc.LibraryHeaderFactory, + moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryHeadersBp2Build, + depsMutators: []android.RegisterMutatorFunc{cc.RegisterDepsBp2Build}, + filesystem: map[string]string{}, + blueprint: soongCcLibraryPreamble + ` cc_library_headers { name: "android-lib" } cc_library_headers { name: "base-lib" } cc_library_headers { name: "darwin-lib" } @@ -183,19 +203,31 @@ cc_library_headers { }, bazel_module: { bp2build_available: true }, }`, - expectedBazelTargets: []string{`cc_library_headers( + expectedBazelTargets: []string{`cc_library_headers( name = "android-lib", - copts = ["-I."], + copts = [ + "-I.", + "-I$(BINDIR)/.", + ], )`, `cc_library_headers( name = "base-lib", - copts = ["-I."], + copts = [ + "-I.", + "-I$(BINDIR)/.", + ], )`, `cc_library_headers( name = "darwin-lib", - copts = ["-I."], + copts = [ + "-I.", + "-I$(BINDIR)/.", + ], )`, `cc_library_headers( name = "foo_headers", - copts = ["-I."], - deps = [":base-lib"] + select({ + copts = [ + "-I.", + "-I$(BINDIR)/.", + ], + implementation_deps = [":base-lib"] + select({ "//build/bazel/platforms/os:android": [":android-lib"], "//build/bazel/platforms/os:darwin": [":darwin-lib"], "//build/bazel/platforms/os:fuchsia": [":fuchsia-lib"], @@ -206,26 +238,41 @@ cc_library_headers { }), )`, `cc_library_headers( name = "fuchsia-lib", - copts = ["-I."], + copts = [ + "-I.", + "-I$(BINDIR)/.", + ], )`, `cc_library_headers( name = "linux-lib", - copts = ["-I."], + copts = [ + "-I.", + "-I$(BINDIR)/.", + ], )`, `cc_library_headers( name = "linux_bionic-lib", - copts = ["-I."], + copts = [ + "-I.", + "-I$(BINDIR)/.", + ], )`, `cc_library_headers( name = "windows-lib", - copts = ["-I."], + copts = [ + "-I.", + "-I$(BINDIR)/.", + ], )`}, - }, - { - description: "cc_library_headers test with os-specific header_libs and export_header_lib_headers props", - moduleTypeUnderTest: "cc_library_headers", - moduleTypeUnderTestFactory: cc.LibraryHeaderFactory, - moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryHeadersBp2Build, - depsMutators: []android.RegisterMutatorFunc{cc.RegisterDepsBp2Build}, - filesystem: map[string]string{}, - bp: soongCcLibraryPreamble + ` + }) +} + +func TestCcLibraryHeadersOsSpecficHeaderLibsExportHeaderLibHeaders(t *testing.T) { + runCcLibraryHeadersTestCase(t, bp2buildTestCase{ + description: "cc_library_headers test with os-specific header_libs and export_header_lib_headers props", + moduleTypeUnderTest: "cc_library_headers", + moduleTypeUnderTestFactory: cc.LibraryHeaderFactory, + moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryHeadersBp2Build, + depsMutators: []android.RegisterMutatorFunc{cc.RegisterDepsBp2Build}, + filesystem: map[string]string{}, + blueprint: soongCcLibraryPreamble + ` cc_library_headers { name: "android-lib" } cc_library_headers { name: "exported-lib" } cc_library_headers { @@ -234,32 +281,45 @@ cc_library_headers { android: { header_libs: ["android-lib"], export_header_lib_headers: ["exported-lib"] }, }, }`, - expectedBazelTargets: []string{`cc_library_headers( + expectedBazelTargets: []string{`cc_library_headers( name = "android-lib", - copts = ["-I."], + copts = [ + "-I.", + "-I$(BINDIR)/.", + ], )`, `cc_library_headers( name = "exported-lib", - copts = ["-I."], + copts = [ + "-I.", + "-I$(BINDIR)/.", + ], )`, `cc_library_headers( name = "foo_headers", - copts = ["-I."], + copts = [ + "-I.", + "-I$(BINDIR)/.", + ], deps = select({ - "//build/bazel/platforms/os:android": [ - ":android-lib", - ":exported-lib", - ], + "//build/bazel/platforms/os:android": [":exported-lib"], + "//conditions:default": [], + }), + implementation_deps = select({ + "//build/bazel/platforms/os:android": [":android-lib"], "//conditions:default": [], }), )`}, - }, - { - description: "cc_library_headers test with arch-specific and target-specific export_system_include_dirs props", - moduleTypeUnderTest: "cc_library_headers", - moduleTypeUnderTestFactory: cc.LibraryHeaderFactory, - moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryHeadersBp2Build, - depsMutators: []android.RegisterMutatorFunc{cc.RegisterDepsBp2Build}, - filesystem: map[string]string{}, - bp: soongCcLibraryPreamble + `cc_library_headers { + }) +} + +func TestCcLibraryHeadersArchAndTargetExportSystemIncludes(t *testing.T) { + runCcLibraryHeadersTestCase(t, bp2buildTestCase{ + description: "cc_library_headers test with arch-specific and target-specific export_system_include_dirs props", + moduleTypeUnderTest: "cc_library_headers", + moduleTypeUnderTestFactory: cc.LibraryHeaderFactory, + moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryHeadersBp2Build, + depsMutators: []android.RegisterMutatorFunc{cc.RegisterDepsBp2Build}, + filesystem: map[string]string{}, + blueprint: soongCcLibraryPreamble + `cc_library_headers { name: "foo_headers", export_system_include_dirs: [ "shared_include_dir", @@ -294,9 +354,12 @@ cc_library_headers { }, }, }`, - expectedBazelTargets: []string{`cc_library_headers( + expectedBazelTargets: []string{`cc_library_headers( name = "foo_headers", - copts = ["-I."], + copts = [ + "-I.", + "-I$(BINDIR)/.", + ], includes = ["shared_include_dir"] + select({ "//build/bazel/platforms/arch:arm": ["arm_include_dir"], "//build/bazel/platforms/arch:x86_64": ["x86_64_include_dir"], @@ -308,65 +371,5 @@ cc_library_headers { "//conditions:default": [], }), )`}, - }, - } - - dir := "." - for _, testCase := range testCases { - filesystem := make(map[string][]byte) - toParse := []string{ - "Android.bp", - } - for f, content := range testCase.filesystem { - if strings.HasSuffix(f, "Android.bp") { - toParse = append(toParse, f) - } - filesystem[f] = []byte(content) - } - config := android.TestConfig(buildDir, nil, testCase.bp, filesystem) - ctx := android.NewTestContext(config) - - // TODO(jingwen): make this default for all bp2build tests - ctx.RegisterBp2BuildConfig(bp2buildConfig) - - cc.RegisterCCBuildComponents(ctx) - ctx.RegisterModuleType("toolchain_library", cc.ToolchainLibraryFactory) - - ctx.RegisterModuleType(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestFactory) - for _, m := range testCase.depsMutators { - ctx.DepsBp2BuildMutators(m) - } - ctx.RegisterBp2BuildMutator(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestBp2BuildMutator) - ctx.RegisterForBazelConversion() - - _, errs := ctx.ParseFileList(dir, toParse) - if Errored(t, testCase.description, errs) { - continue - } - _, errs = ctx.ResolveDependencies(config) - if Errored(t, testCase.description, errs) { - continue - } - - checkDir := dir - if testCase.dir != "" { - checkDir = testCase.dir - } - codegenCtx := NewCodegenContext(config, *ctx.Context, Bp2Build) - bazelTargets := generateBazelTargetsForDir(codegenCtx, checkDir) - if actualCount, expectedCount := len(bazelTargets), len(testCase.expectedBazelTargets); actualCount != expectedCount { - t.Errorf("%s: Expected %d bazel target, got %d", testCase.description, expectedCount, actualCount) - } else { - for i, target := range bazelTargets { - if w, g := testCase.expectedBazelTargets[i], target.content; w != g { - t.Errorf( - "%s: Expected generated Bazel target to be '%s', got '%s'", - testCase.description, - w, - g, - ) - } - } - } - } + }) } diff --git a/bp2build/cc_library_static_conversion_test.go b/bp2build/cc_library_static_conversion_test.go index d082db1bd..62084a544 100644 --- a/bp2build/cc_library_static_conversion_test.go +++ b/bp2build/cc_library_static_conversion_test.go @@ -17,7 +17,8 @@ package bp2build import ( "android/soong/android" "android/soong/cc" - "strings" + "android/soong/genrule" + "testing" ) @@ -67,45 +68,44 @@ func TestCcLibraryStaticLoadStatement(t *testing.T) { } -func TestCcLibraryStaticBp2Build(t *testing.T) { - testCases := []struct { - description string - moduleTypeUnderTest string - moduleTypeUnderTestFactory android.ModuleFactory - moduleTypeUnderTestBp2BuildMutator func(android.TopDownMutatorContext) - preArchMutators []android.RegisterMutatorFunc - depsMutators []android.RegisterMutatorFunc - bp string - expectedBazelTargets []string - filesystem map[string]string - dir string - }{ - { - description: "cc_library_static test", - moduleTypeUnderTest: "cc_library_static", - moduleTypeUnderTestFactory: cc.LibraryStaticFactory, - moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build, - filesystem: map[string]string{ - // NOTE: include_dir headers *should not* appear in Bazel hdrs later (?) - "include_dir_1/include_dir_1_a.h": "", - "include_dir_1/include_dir_1_b.h": "", - "include_dir_2/include_dir_2_a.h": "", - "include_dir_2/include_dir_2_b.h": "", - // NOTE: local_include_dir headers *should not* appear in Bazel hdrs later (?) - "local_include_dir_1/local_include_dir_1_a.h": "", - "local_include_dir_1/local_include_dir_1_b.h": "", - "local_include_dir_2/local_include_dir_2_a.h": "", - "local_include_dir_2/local_include_dir_2_b.h": "", - // NOTE: export_include_dir headers *should* appear in Bazel hdrs later - "export_include_dir_1/export_include_dir_1_a.h": "", - "export_include_dir_1/export_include_dir_1_b.h": "", - "export_include_dir_2/export_include_dir_2_a.h": "", - "export_include_dir_2/export_include_dir_2_b.h": "", - // NOTE: Soong implicitly includes headers in the current directory - "implicit_include_1.h": "", - "implicit_include_2.h": "", - }, - bp: soongCcLibraryStaticPreamble + ` +func registerCcLibraryStaticModuleTypes(ctx android.RegistrationContext) { + cc.RegisterCCBuildComponents(ctx) + ctx.RegisterModuleType("toolchain_library", cc.ToolchainLibraryFactory) + ctx.RegisterModuleType("cc_library_headers", cc.LibraryHeaderFactory) + ctx.RegisterModuleType("genrule", genrule.GenRuleFactory) +} + +func runCcLibraryStaticTestCase(t *testing.T, tc bp2buildTestCase) { + runBp2BuildTestCase(t, registerCcLibraryStaticModuleTypes, tc) +} + +func TestCcLibraryStaticSimple(t *testing.T) { + runCcLibraryStaticTestCase(t, bp2buildTestCase{ + description: "cc_library_static test", + moduleTypeUnderTest: "cc_library_static", + moduleTypeUnderTestFactory: cc.LibraryStaticFactory, + moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build, + filesystem: map[string]string{ + // NOTE: include_dir headers *should not* appear in Bazel hdrs later (?) + "include_dir_1/include_dir_1_a.h": "", + "include_dir_1/include_dir_1_b.h": "", + "include_dir_2/include_dir_2_a.h": "", + "include_dir_2/include_dir_2_b.h": "", + // NOTE: local_include_dir headers *should not* appear in Bazel hdrs later (?) + "local_include_dir_1/local_include_dir_1_a.h": "", + "local_include_dir_1/local_include_dir_1_b.h": "", + "local_include_dir_2/local_include_dir_2_a.h": "", + "local_include_dir_2/local_include_dir_2_b.h": "", + // NOTE: export_include_dir headers *should* appear in Bazel hdrs later + "export_include_dir_1/export_include_dir_1_a.h": "", + "export_include_dir_1/export_include_dir_1_b.h": "", + "export_include_dir_2/export_include_dir_2_a.h": "", + "export_include_dir_2/export_include_dir_2_b.h": "", + // NOTE: Soong implicitly includes headers in the current directory + "implicit_include_1.h": "", + "implicit_include_2.h": "", + }, + blueprint: soongCcLibraryStaticPreamble + ` cc_library_headers { name: "header_lib_1", export_include_dirs: ["header_lib_1"], @@ -163,8 +163,8 @@ cc_library_static { "local_include_dir_2", ], export_include_dirs: [ - "export_include_dir_1", - "export_include_dir_2" + "export_include_dir_1", + "export_include_dir_2" ], header_libs: [ "header_lib_1", @@ -173,18 +173,23 @@ cc_library_static { // TODO: Also support export_header_lib_headers }`, - expectedBazelTargets: []string{`cc_library_static( + expectedBazelTargets: []string{`cc_library_static( name = "foo_static", copts = [ "-Dflag1", "-Dflag2", "-Iinclude_dir_1", + "-I$(BINDIR)/include_dir_1", "-Iinclude_dir_2", + "-I$(BINDIR)/include_dir_2", "-Ilocal_include_dir_1", + "-I$(BINDIR)/local_include_dir_1", "-Ilocal_include_dir_2", + "-I$(BINDIR)/local_include_dir_2", "-I.", + "-I$(BINDIR)/.", ], - deps = [ + implementation_deps = [ ":header_lib_1", ":header_lib_2", ":static_lib_1", @@ -205,46 +210,61 @@ cc_library_static { ], )`, `cc_library_static( name = "static_lib_1", - copts = ["-I."], + copts = [ + "-I.", + "-I$(BINDIR)/.", + ], linkstatic = True, srcs = ["static_lib_1.cc"], )`, `cc_library_static( name = "static_lib_2", - copts = ["-I."], + copts = [ + "-I.", + "-I$(BINDIR)/.", + ], linkstatic = True, srcs = ["static_lib_2.cc"], )`, `cc_library_static( name = "whole_static_lib_1", - copts = ["-I."], + copts = [ + "-I.", + "-I$(BINDIR)/.", + ], linkstatic = True, srcs = ["whole_static_lib_1.cc"], )`, `cc_library_static( name = "whole_static_lib_2", - copts = ["-I."], + copts = [ + "-I.", + "-I$(BINDIR)/.", + ], linkstatic = True, srcs = ["whole_static_lib_2.cc"], )`}, + }) +} + +func TestCcLibraryStaticSubpackage(t *testing.T) { + runCcLibraryStaticTestCase(t, bp2buildTestCase{ + description: "cc_library_static subpackage test", + moduleTypeUnderTest: "cc_library_static", + moduleTypeUnderTestFactory: cc.LibraryStaticFactory, + moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build, + filesystem: map[string]string{ + // subpackage with subdirectory + "subpackage/Android.bp": "", + "subpackage/subpackage_header.h": "", + "subpackage/subdirectory/subdirectory_header.h": "", + // subsubpackage with subdirectory + "subpackage/subsubpackage/Android.bp": "", + "subpackage/subsubpackage/subsubpackage_header.h": "", + "subpackage/subsubpackage/subdirectory/subdirectory_header.h": "", + // subsubsubpackage with subdirectory + "subpackage/subsubpackage/subsubsubpackage/Android.bp": "", + "subpackage/subsubpackage/subsubsubpackage/subsubsubpackage_header.h": "", + "subpackage/subsubpackage/subsubsubpackage/subdirectory/subdirectory_header.h": "", }, - { - description: "cc_library_static subpackage test", - moduleTypeUnderTest: "cc_library_static", - moduleTypeUnderTestFactory: cc.LibraryStaticFactory, - moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build, - filesystem: map[string]string{ - // subpackage with subdirectory - "subpackage/Android.bp": "", - "subpackage/subpackage_header.h": "", - "subpackage/subdirectory/subdirectory_header.h": "", - // subsubpackage with subdirectory - "subpackage/subsubpackage/Android.bp": "", - "subpackage/subsubpackage/subsubpackage_header.h": "", - "subpackage/subsubpackage/subdirectory/subdirectory_header.h": "", - // subsubsubpackage with subdirectory - "subpackage/subsubpackage/subsubsubpackage/Android.bp": "", - "subpackage/subsubpackage/subsubsubpackage/subsubsubpackage_header.h": "", - "subpackage/subsubpackage/subsubsubpackage/subdirectory/subdirectory_header.h": "", - }, - bp: soongCcLibraryStaticPreamble + ` + blueprint: soongCcLibraryStaticPreamble + ` cc_library_static { name: "foo_static", srcs: [ @@ -253,70 +273,87 @@ cc_library_static { "subpackage", ], }`, - expectedBazelTargets: []string{`cc_library_static( + expectedBazelTargets: []string{`cc_library_static( name = "foo_static", copts = [ "-Isubpackage", + "-I$(BINDIR)/subpackage", "-I.", + "-I$(BINDIR)/.", ], linkstatic = True, )`}, + }) +} + +func TestCcLibraryStaticExportIncludeDir(t *testing.T) { + runCcLibraryStaticTestCase(t, bp2buildTestCase{ + description: "cc_library_static export include dir", + moduleTypeUnderTest: "cc_library_static", + moduleTypeUnderTestFactory: cc.LibraryStaticFactory, + moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build, + filesystem: map[string]string{ + // subpackage with subdirectory + "subpackage/Android.bp": "", + "subpackage/subpackage_header.h": "", + "subpackage/subdirectory/subdirectory_header.h": "", }, - { - description: "cc_library_static export include dir", - moduleTypeUnderTest: "cc_library_static", - moduleTypeUnderTestFactory: cc.LibraryStaticFactory, - moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build, - filesystem: map[string]string{ - // subpackage with subdirectory - "subpackage/Android.bp": "", - "subpackage/subpackage_header.h": "", - "subpackage/subdirectory/subdirectory_header.h": "", - }, - bp: soongCcLibraryStaticPreamble + ` + blueprint: soongCcLibraryStaticPreamble + ` cc_library_static { name: "foo_static", export_include_dirs: ["subpackage"], }`, - expectedBazelTargets: []string{`cc_library_static( + expectedBazelTargets: []string{`cc_library_static( name = "foo_static", - copts = ["-I."], + copts = [ + "-I.", + "-I$(BINDIR)/.", + ], includes = ["subpackage"], linkstatic = True, )`}, + }) +} + +func TestCcLibraryStaticExportSystemIncludeDir(t *testing.T) { + runCcLibraryStaticTestCase(t, bp2buildTestCase{ + description: "cc_library_static export system include dir", + moduleTypeUnderTest: "cc_library_static", + moduleTypeUnderTestFactory: cc.LibraryStaticFactory, + moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build, + filesystem: map[string]string{ + // subpackage with subdirectory + "subpackage/Android.bp": "", + "subpackage/subpackage_header.h": "", + "subpackage/subdirectory/subdirectory_header.h": "", }, - { - description: "cc_library_static export system include dir", - moduleTypeUnderTest: "cc_library_static", - moduleTypeUnderTestFactory: cc.LibraryStaticFactory, - moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build, - filesystem: map[string]string{ - // subpackage with subdirectory - "subpackage/Android.bp": "", - "subpackage/subpackage_header.h": "", - "subpackage/subdirectory/subdirectory_header.h": "", - }, - bp: soongCcLibraryStaticPreamble + ` + blueprint: soongCcLibraryStaticPreamble + ` cc_library_static { name: "foo_static", export_system_include_dirs: ["subpackage"], }`, - expectedBazelTargets: []string{`cc_library_static( + expectedBazelTargets: []string{`cc_library_static( name = "foo_static", - copts = ["-I."], + copts = [ + "-I.", + "-I$(BINDIR)/.", + ], includes = ["subpackage"], linkstatic = True, )`}, - }, - { - description: "cc_library_static include_dirs, local_include_dirs, export_include_dirs (b/183742505)", - moduleTypeUnderTest: "cc_library_static", - moduleTypeUnderTestFactory: cc.LibraryStaticFactory, - moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build, - dir: "subpackage", - filesystem: map[string]string{ - // subpackage with subdirectory - "subpackage/Android.bp": ` + }) +} + +func TestCcLibraryStaticManyIncludeDirs(t *testing.T) { + runCcLibraryStaticTestCase(t, bp2buildTestCase{ + description: "cc_library_static include_dirs, local_include_dirs, export_include_dirs (b/183742505)", + moduleTypeUnderTest: "cc_library_static", + moduleTypeUnderTestFactory: cc.LibraryStaticFactory, + moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build, + dir: "subpackage", + filesystem: map[string]string{ + // subpackage with subdirectory + "subpackage/Android.bp": ` cc_library_static { name: "foo_static", // include_dirs are workspace/root relative @@ -330,101 +367,123 @@ cc_library_static { include_build_directory: true, bazel_module: { bp2build_available: true }, }`, - "subpackage/subsubpackage/header.h": "", - "subpackage/subsubpackage2/header.h": "", - "subpackage/exported_subsubpackage/header.h": "", - "subpackage2/header.h": "", - "subpackage3/subsubpackage/header.h": "", - }, - bp: soongCcLibraryStaticPreamble, - expectedBazelTargets: []string{`cc_library_static( + "subpackage/subsubpackage/header.h": "", + "subpackage/subsubpackage2/header.h": "", + "subpackage/exported_subsubpackage/header.h": "", + "subpackage2/header.h": "", + "subpackage3/subsubpackage/header.h": "", + }, + blueprint: soongCcLibraryStaticPreamble, + expectedBazelTargets: []string{`cc_library_static( name = "foo_static", copts = [ "-Isubpackage/subsubpackage", + "-I$(BINDIR)/subpackage/subsubpackage", "-Isubpackage2", + "-I$(BINDIR)/subpackage2", "-Isubpackage3/subsubpackage", + "-I$(BINDIR)/subpackage3/subsubpackage", "-Isubpackage/subsubpackage2", + "-I$(BINDIR)/subpackage/subsubpackage2", "-Isubpackage", + "-I$(BINDIR)/subpackage", ], includes = ["./exported_subsubpackage"], linkstatic = True, )`}, + }) +} + +func TestCcLibraryStaticIncludeBuildDirectoryDisabled(t *testing.T) { + runCcLibraryStaticTestCase(t, bp2buildTestCase{ + description: "cc_library_static include_build_directory disabled", + moduleTypeUnderTest: "cc_library_static", + moduleTypeUnderTestFactory: cc.LibraryStaticFactory, + moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build, + filesystem: map[string]string{ + // subpackage with subdirectory + "subpackage/Android.bp": "", + "subpackage/subpackage_header.h": "", + "subpackage/subdirectory/subdirectory_header.h": "", }, - { - description: "cc_library_static include_build_directory disabled", - moduleTypeUnderTest: "cc_library_static", - moduleTypeUnderTestFactory: cc.LibraryStaticFactory, - moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build, - filesystem: map[string]string{ - // subpackage with subdirectory - "subpackage/Android.bp": "", - "subpackage/subpackage_header.h": "", - "subpackage/subdirectory/subdirectory_header.h": "", - }, - bp: soongCcLibraryStaticPreamble + ` + blueprint: soongCcLibraryStaticPreamble + ` cc_library_static { name: "foo_static", include_dirs: ["subpackage"], // still used, but local_include_dirs is recommended local_include_dirs: ["subpackage2"], include_build_directory: false, }`, - expectedBazelTargets: []string{`cc_library_static( + expectedBazelTargets: []string{`cc_library_static( name = "foo_static", copts = [ "-Isubpackage", + "-I$(BINDIR)/subpackage", "-Isubpackage2", + "-I$(BINDIR)/subpackage2", ], linkstatic = True, )`}, + }) +} + +func TestCcLibraryStaticIncludeBuildDirectoryEnabled(t *testing.T) { + runCcLibraryStaticTestCase(t, bp2buildTestCase{ + description: "cc_library_static include_build_directory enabled", + moduleTypeUnderTest: "cc_library_static", + moduleTypeUnderTestFactory: cc.LibraryStaticFactory, + moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build, + filesystem: map[string]string{ + // subpackage with subdirectory + "subpackage/Android.bp": "", + "subpackage/subpackage_header.h": "", + "subpackage2/Android.bp": "", + "subpackage2/subpackage2_header.h": "", + "subpackage/subdirectory/subdirectory_header.h": "", }, - { - description: "cc_library_static include_build_directory enabled", - moduleTypeUnderTest: "cc_library_static", - moduleTypeUnderTestFactory: cc.LibraryStaticFactory, - moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build, - filesystem: map[string]string{ - // subpackage with subdirectory - "subpackage/Android.bp": "", - "subpackage/subpackage_header.h": "", - "subpackage2/Android.bp": "", - "subpackage2/subpackage2_header.h": "", - "subpackage/subdirectory/subdirectory_header.h": "", - }, - bp: soongCcLibraryStaticPreamble + ` + blueprint: soongCcLibraryStaticPreamble + ` cc_library_static { name: "foo_static", include_dirs: ["subpackage"], // still used, but local_include_dirs is recommended local_include_dirs: ["subpackage2"], include_build_directory: true, }`, - expectedBazelTargets: []string{`cc_library_static( + expectedBazelTargets: []string{`cc_library_static( name = "foo_static", copts = [ "-Isubpackage", + "-I$(BINDIR)/subpackage", "-Isubpackage2", + "-I$(BINDIR)/subpackage2", "-I.", + "-I$(BINDIR)/.", ], linkstatic = True, )`}, - }, - { - description: "cc_library_static arch-specific static_libs", - moduleTypeUnderTest: "cc_library_static", - moduleTypeUnderTestFactory: cc.LibraryStaticFactory, - moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build, - depsMutators: []android.RegisterMutatorFunc{cc.RegisterDepsBp2Build}, - filesystem: map[string]string{}, - bp: soongCcLibraryStaticPreamble + ` + }) +} + +func TestCcLibraryStaticArchSpecificStaticLib(t *testing.T) { + runCcLibraryStaticTestCase(t, bp2buildTestCase{ + description: "cc_library_static arch-specific static_libs", + moduleTypeUnderTest: "cc_library_static", + moduleTypeUnderTestFactory: cc.LibraryStaticFactory, + moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build, + depsMutators: []android.RegisterMutatorFunc{cc.RegisterDepsBp2Build}, + filesystem: map[string]string{}, + blueprint: soongCcLibraryStaticPreamble + ` cc_library_static { name: "static_dep" } cc_library_static { name: "static_dep2" } cc_library_static { name: "foo_static", arch: { arm64: { static_libs: ["static_dep"], whole_static_libs: ["static_dep2"] } }, }`, - expectedBazelTargets: []string{`cc_library_static( + expectedBazelTargets: []string{`cc_library_static( name = "foo_static", - copts = ["-I."], - deps = select({ + copts = [ + "-I.", + "-I$(BINDIR)/.", + ], + implementation_deps = select({ "//build/bazel/platforms/arch:arm64": [":static_dep"], "//conditions:default": [], }), @@ -435,32 +494,44 @@ cc_library_static { }), )`, `cc_library_static( name = "static_dep", - copts = ["-I."], + copts = [ + "-I.", + "-I$(BINDIR)/.", + ], linkstatic = True, )`, `cc_library_static( name = "static_dep2", - copts = ["-I."], + copts = [ + "-I.", + "-I$(BINDIR)/.", + ], linkstatic = True, )`}, - }, - { - description: "cc_library_static os-specific static_libs", - moduleTypeUnderTest: "cc_library_static", - moduleTypeUnderTestFactory: cc.LibraryStaticFactory, - moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build, - depsMutators: []android.RegisterMutatorFunc{cc.RegisterDepsBp2Build}, - filesystem: map[string]string{}, - bp: soongCcLibraryStaticPreamble + ` + }) +} + +func TestCcLibraryStaticOsSpecificStaticLib(t *testing.T) { + runCcLibraryStaticTestCase(t, bp2buildTestCase{ + description: "cc_library_static os-specific static_libs", + moduleTypeUnderTest: "cc_library_static", + moduleTypeUnderTestFactory: cc.LibraryStaticFactory, + moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build, + depsMutators: []android.RegisterMutatorFunc{cc.RegisterDepsBp2Build}, + filesystem: map[string]string{}, + blueprint: soongCcLibraryStaticPreamble + ` cc_library_static { name: "static_dep" } cc_library_static { name: "static_dep2" } cc_library_static { name: "foo_static", target: { android: { static_libs: ["static_dep"], whole_static_libs: ["static_dep2"] } }, }`, - expectedBazelTargets: []string{`cc_library_static( + expectedBazelTargets: []string{`cc_library_static( name = "foo_static", - copts = ["-I."], - deps = select({ + copts = [ + "-I.", + "-I$(BINDIR)/.", + ], + implementation_deps = select({ "//build/bazel/platforms/os:android": [":static_dep"], "//conditions:default": [], }), @@ -471,22 +542,31 @@ cc_library_static { }), )`, `cc_library_static( name = "static_dep", - copts = ["-I."], + copts = [ + "-I.", + "-I$(BINDIR)/.", + ], linkstatic = True, )`, `cc_library_static( name = "static_dep2", - copts = ["-I."], + copts = [ + "-I.", + "-I$(BINDIR)/.", + ], linkstatic = True, )`}, - }, - { - description: "cc_library_static base, arch and os-specific static_libs", - moduleTypeUnderTest: "cc_library_static", - moduleTypeUnderTestFactory: cc.LibraryStaticFactory, - moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build, - depsMutators: []android.RegisterMutatorFunc{cc.RegisterDepsBp2Build}, - filesystem: map[string]string{}, - bp: soongCcLibraryStaticPreamble + ` + }) +} + +func TestCcLibraryStaticBaseArchOsSpecificStaticLib(t *testing.T) { + runCcLibraryStaticTestCase(t, bp2buildTestCase{ + description: "cc_library_static base, arch and os-specific static_libs", + moduleTypeUnderTest: "cc_library_static", + moduleTypeUnderTestFactory: cc.LibraryStaticFactory, + moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build, + depsMutators: []android.RegisterMutatorFunc{cc.RegisterDepsBp2Build}, + filesystem: map[string]string{}, + blueprint: soongCcLibraryStaticPreamble + ` cc_library_static { name: "static_dep" } cc_library_static { name: "static_dep2" } cc_library_static { name: "static_dep3" } @@ -498,10 +578,13 @@ cc_library_static { target: { android: { static_libs: ["static_dep3"] } }, arch: { arm64: { static_libs: ["static_dep4"] } }, }`, - expectedBazelTargets: []string{`cc_library_static( + expectedBazelTargets: []string{`cc_library_static( name = "foo_static", - copts = ["-I."], - deps = [":static_dep"] + select({ + copts = [ + "-I.", + "-I$(BINDIR)/.", + ], + implementation_deps = [":static_dep"] + select({ "//build/bazel/platforms/arch:arm64": [":static_dep4"], "//conditions:default": [], }) + select({ @@ -512,88 +595,115 @@ cc_library_static { whole_archive_deps = [":static_dep2"], )`, `cc_library_static( name = "static_dep", - copts = ["-I."], + copts = [ + "-I.", + "-I$(BINDIR)/.", + ], linkstatic = True, )`, `cc_library_static( name = "static_dep2", - copts = ["-I."], + copts = [ + "-I.", + "-I$(BINDIR)/.", + ], linkstatic = True, )`, `cc_library_static( name = "static_dep3", - copts = ["-I."], + copts = [ + "-I.", + "-I$(BINDIR)/.", + ], linkstatic = True, )`, `cc_library_static( name = "static_dep4", - copts = ["-I."], + copts = [ + "-I.", + "-I$(BINDIR)/.", + ], linkstatic = True, )`}, + }) +} + +func TestCcLibraryStaticSimpleExcludeSrcs(t *testing.T) { + runCcLibraryStaticTestCase(t, bp2buildTestCase{ + description: "cc_library_static simple exclude_srcs", + moduleTypeUnderTest: "cc_library_static", + moduleTypeUnderTestFactory: cc.LibraryStaticFactory, + moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build, + depsMutators: []android.RegisterMutatorFunc{cc.RegisterDepsBp2Build}, + filesystem: map[string]string{ + "common.c": "", + "foo-a.c": "", + "foo-excluded.c": "", }, - { - description: "cc_library_static simple exclude_srcs", - moduleTypeUnderTest: "cc_library_static", - moduleTypeUnderTestFactory: cc.LibraryStaticFactory, - moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build, - depsMutators: []android.RegisterMutatorFunc{cc.RegisterDepsBp2Build}, - filesystem: map[string]string{ - "common.c": "", - "foo-a.c": "", - "foo-excluded.c": "", - }, - bp: soongCcLibraryStaticPreamble + ` + blueprint: soongCcLibraryStaticPreamble + ` cc_library_static { name: "foo_static", srcs: ["common.c", "foo-*.c"], exclude_srcs: ["foo-excluded.c"], }`, - expectedBazelTargets: []string{`cc_library_static( + expectedBazelTargets: []string{`cc_library_static( name = "foo_static", - copts = ["-I."], + copts = [ + "-I.", + "-I$(BINDIR)/.", + ], linkstatic = True, srcs = [ "common.c", "foo-a.c", ], )`}, + }) +} + +func TestCcLibraryStaticOneArchSrcs(t *testing.T) { + runCcLibraryStaticTestCase(t, bp2buildTestCase{ + description: "cc_library_static one arch specific srcs", + moduleTypeUnderTest: "cc_library_static", + moduleTypeUnderTestFactory: cc.LibraryStaticFactory, + moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build, + depsMutators: []android.RegisterMutatorFunc{cc.RegisterDepsBp2Build}, + filesystem: map[string]string{ + "common.c": "", + "foo-arm.c": "", }, - { - description: "cc_library_static one arch specific srcs", - moduleTypeUnderTest: "cc_library_static", - moduleTypeUnderTestFactory: cc.LibraryStaticFactory, - moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build, - depsMutators: []android.RegisterMutatorFunc{cc.RegisterDepsBp2Build}, - filesystem: map[string]string{ - "common.c": "", - "foo-arm.c": "", - }, - bp: soongCcLibraryStaticPreamble + ` + blueprint: soongCcLibraryStaticPreamble + ` cc_library_static { name: "foo_static", srcs: ["common.c"], arch: { arm: { srcs: ["foo-arm.c"] } } }`, - expectedBazelTargets: []string{`cc_library_static( + expectedBazelTargets: []string{`cc_library_static( name = "foo_static", - copts = ["-I."], + copts = [ + "-I.", + "-I$(BINDIR)/.", + ], linkstatic = True, srcs = ["common.c"] + select({ "//build/bazel/platforms/arch:arm": ["foo-arm.c"], "//conditions:default": [], }), )`}, + }) +} + +func TestCcLibraryStaticOneArchSrcsExcludeSrcs(t *testing.T) { + runCcLibraryStaticTestCase(t, bp2buildTestCase{ + description: "cc_library_static one arch specific srcs and exclude_srcs", + moduleTypeUnderTest: "cc_library_static", + moduleTypeUnderTestFactory: cc.LibraryStaticFactory, + moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build, + depsMutators: []android.RegisterMutatorFunc{cc.RegisterDepsBp2Build}, + filesystem: map[string]string{ + "common.c": "", + "for-arm.c": "", + "not-for-arm.c": "", + "not-for-anything.c": "", }, - { - description: "cc_library_static one arch specific srcs and exclude_srcs", - moduleTypeUnderTest: "cc_library_static", - moduleTypeUnderTestFactory: cc.LibraryStaticFactory, - moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build, - depsMutators: []android.RegisterMutatorFunc{cc.RegisterDepsBp2Build}, - filesystem: map[string]string{ - "common.c": "", - "for-arm.c": "", - "not-for-arm.c": "", - "not-for-anything.c": "", - }, - bp: soongCcLibraryStaticPreamble + ` + blueprint: soongCcLibraryStaticPreamble + ` cc_library_static { name: "foo_static", srcs: ["common.c", "not-for-*.c"], @@ -602,30 +712,36 @@ cc_library_static { arm: { srcs: ["for-arm.c"], exclude_srcs: ["not-for-arm.c"] }, }, }`, - expectedBazelTargets: []string{`cc_library_static( + expectedBazelTargets: []string{`cc_library_static( name = "foo_static", - copts = ["-I."], + copts = [ + "-I.", + "-I$(BINDIR)/.", + ], linkstatic = True, srcs = ["common.c"] + select({ "//build/bazel/platforms/arch:arm": ["for-arm.c"], "//conditions:default": ["not-for-arm.c"], }), )`}, + }) +} + +func TestCcLibraryStaticTwoArchExcludeSrcs(t *testing.T) { + runCcLibraryStaticTestCase(t, bp2buildTestCase{ + description: "cc_library_static arch specific exclude_srcs for 2 architectures", + moduleTypeUnderTest: "cc_library_static", + moduleTypeUnderTestFactory: cc.LibraryStaticFactory, + moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build, + depsMutators: []android.RegisterMutatorFunc{cc.RegisterDepsBp2Build}, + filesystem: map[string]string{ + "common.c": "", + "for-arm.c": "", + "for-x86.c": "", + "not-for-arm.c": "", + "not-for-x86.c": "", }, - { - description: "cc_library_static arch specific exclude_srcs for 2 architectures", - moduleTypeUnderTest: "cc_library_static", - moduleTypeUnderTestFactory: cc.LibraryStaticFactory, - moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build, - depsMutators: []android.RegisterMutatorFunc{cc.RegisterDepsBp2Build}, - filesystem: map[string]string{ - "common.c": "", - "for-arm.c": "", - "for-x86.c": "", - "not-for-arm.c": "", - "not-for-x86.c": "", - }, - bp: soongCcLibraryStaticPreamble + ` + blueprint: soongCcLibraryStaticPreamble + ` cc_library_static { name: "foo_static", srcs: ["common.c", "not-for-*.c"], @@ -635,9 +751,12 @@ cc_library_static { x86: { srcs: ["for-x86.c"], exclude_srcs: ["not-for-x86.c"] }, }, } `, - expectedBazelTargets: []string{`cc_library_static( + expectedBazelTargets: []string{`cc_library_static( name = "foo_static", - copts = ["-I."], + copts = [ + "-I.", + "-I$(BINDIR)/.", + ], linkstatic = True, srcs = ["common.c"] + select({ "//build/bazel/platforms/arch:arm": [ @@ -654,26 +773,28 @@ cc_library_static { ], }), )`}, + }) +} +func TestCcLibraryStaticFourArchExcludeSrcs(t *testing.T) { + runCcLibraryStaticTestCase(t, bp2buildTestCase{ + description: "cc_library_static arch specific exclude_srcs for 4 architectures", + moduleTypeUnderTest: "cc_library_static", + moduleTypeUnderTestFactory: cc.LibraryStaticFactory, + moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build, + depsMutators: []android.RegisterMutatorFunc{cc.RegisterDepsBp2Build}, + filesystem: map[string]string{ + "common.c": "", + "for-arm.c": "", + "for-arm64.c": "", + "for-x86.c": "", + "for-x86_64.c": "", + "not-for-arm.c": "", + "not-for-arm64.c": "", + "not-for-x86.c": "", + "not-for-x86_64.c": "", + "not-for-everything.c": "", }, - { - description: "cc_library_static arch specific exclude_srcs for 4 architectures", - moduleTypeUnderTest: "cc_library_static", - moduleTypeUnderTestFactory: cc.LibraryStaticFactory, - moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build, - depsMutators: []android.RegisterMutatorFunc{cc.RegisterDepsBp2Build}, - filesystem: map[string]string{ - "common.c": "", - "for-arm.c": "", - "for-arm64.c": "", - "for-x86.c": "", - "for-x86_64.c": "", - "not-for-arm.c": "", - "not-for-arm64.c": "", - "not-for-x86.c": "", - "not-for-x86_64.c": "", - "not-for-everything.c": "", - }, - bp: soongCcLibraryStaticPreamble + ` + blueprint: soongCcLibraryStaticPreamble + ` cc_library_static { name: "foo_static", srcs: ["common.c", "not-for-*.c"], @@ -685,9 +806,12 @@ cc_library_static { x86_64: { srcs: ["for-x86_64.c"], exclude_srcs: ["not-for-x86_64.c"] }, }, } `, - expectedBazelTargets: []string{`cc_library_static( + expectedBazelTargets: []string{`cc_library_static( name = "foo_static", - copts = ["-I."], + copts = [ + "-I.", + "-I$(BINDIR)/.", + ], linkstatic = True, srcs = ["common.c"] + select({ "//build/bazel/platforms/arch:arm": [ @@ -722,43 +846,55 @@ cc_library_static { ], }), )`}, - }, - { - description: "cc_library_static multiple dep same name panic", - moduleTypeUnderTest: "cc_library_static", - moduleTypeUnderTestFactory: cc.LibraryStaticFactory, - moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build, - depsMutators: []android.RegisterMutatorFunc{cc.RegisterDepsBp2Build}, - filesystem: map[string]string{}, - bp: soongCcLibraryStaticPreamble + ` + }) +} + +func TestCcLibraryStaticMultipleDepSameName(t *testing.T) { + runCcLibraryStaticTestCase(t, bp2buildTestCase{ + description: "cc_library_static multiple dep same name panic", + moduleTypeUnderTest: "cc_library_static", + moduleTypeUnderTestFactory: cc.LibraryStaticFactory, + moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build, + depsMutators: []android.RegisterMutatorFunc{cc.RegisterDepsBp2Build}, + filesystem: map[string]string{}, + blueprint: soongCcLibraryStaticPreamble + ` cc_library_static { name: "static_dep" } cc_library_static { name: "foo_static", static_libs: ["static_dep", "static_dep"], }`, - expectedBazelTargets: []string{`cc_library_static( + expectedBazelTargets: []string{`cc_library_static( name = "foo_static", - copts = ["-I."], - deps = [":static_dep"], + copts = [ + "-I.", + "-I$(BINDIR)/.", + ], + implementation_deps = [":static_dep"], linkstatic = True, )`, `cc_library_static( name = "static_dep", - copts = ["-I."], + copts = [ + "-I.", + "-I$(BINDIR)/.", + ], linkstatic = True, )`}, + }) +} + +func TestCcLibraryStaticOneMultilibSrcsExcludeSrcs(t *testing.T) { + runCcLibraryStaticTestCase(t, bp2buildTestCase{ + description: "cc_library_static 1 multilib srcs and exclude_srcs", + moduleTypeUnderTest: "cc_library_static", + moduleTypeUnderTestFactory: cc.LibraryStaticFactory, + moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build, + depsMutators: []android.RegisterMutatorFunc{cc.RegisterDepsBp2Build}, + filesystem: map[string]string{ + "common.c": "", + "for-lib32.c": "", + "not-for-lib32.c": "", }, - { - description: "cc_library_static 1 multilib srcs and exclude_srcs", - moduleTypeUnderTest: "cc_library_static", - moduleTypeUnderTestFactory: cc.LibraryStaticFactory, - moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build, - depsMutators: []android.RegisterMutatorFunc{cc.RegisterDepsBp2Build}, - filesystem: map[string]string{ - "common.c": "", - "for-lib32.c": "", - "not-for-lib32.c": "", - }, - bp: soongCcLibraryStaticPreamble + ` + blueprint: soongCcLibraryStaticPreamble + ` cc_library_static { name: "foo_static", srcs: ["common.c", "not-for-*.c"], @@ -766,9 +902,12 @@ cc_library_static { lib32: { srcs: ["for-lib32.c"], exclude_srcs: ["not-for-lib32.c"] }, }, } `, - expectedBazelTargets: []string{`cc_library_static( + expectedBazelTargets: []string{`cc_library_static( name = "foo_static", - copts = ["-I."], + copts = [ + "-I.", + "-I$(BINDIR)/.", + ], linkstatic = True, srcs = ["common.c"] + select({ "//build/bazel/platforms/arch:arm": ["for-lib32.c"], @@ -776,21 +915,24 @@ cc_library_static { "//conditions:default": ["not-for-lib32.c"], }), )`}, + }) +} + +func TestCcLibraryStaticTwoMultilibSrcsExcludeSrcs(t *testing.T) { + runCcLibraryStaticTestCase(t, bp2buildTestCase{ + description: "cc_library_static 2 multilib srcs and exclude_srcs", + moduleTypeUnderTest: "cc_library_static", + moduleTypeUnderTestFactory: cc.LibraryStaticFactory, + moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build, + depsMutators: []android.RegisterMutatorFunc{cc.RegisterDepsBp2Build}, + filesystem: map[string]string{ + "common.c": "", + "for-lib32.c": "", + "for-lib64.c": "", + "not-for-lib32.c": "", + "not-for-lib64.c": "", }, - { - description: "cc_library_static 2 multilib srcs and exclude_srcs", - moduleTypeUnderTest: "cc_library_static", - moduleTypeUnderTestFactory: cc.LibraryStaticFactory, - moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build, - depsMutators: []android.RegisterMutatorFunc{cc.RegisterDepsBp2Build}, - filesystem: map[string]string{ - "common.c": "", - "for-lib32.c": "", - "for-lib64.c": "", - "not-for-lib32.c": "", - "not-for-lib64.c": "", - }, - bp: soongCcLibraryStaticPreamble + ` + blueprint: soongCcLibraryStaticPreamble + ` cc_library_static { name: "foo_static2", srcs: ["common.c", "not-for-*.c"], @@ -799,9 +941,12 @@ cc_library_static { lib64: { srcs: ["for-lib64.c"], exclude_srcs: ["not-for-lib64.c"] }, }, } `, - expectedBazelTargets: []string{`cc_library_static( + expectedBazelTargets: []string{`cc_library_static( name = "foo_static2", - copts = ["-I."], + copts = [ + "-I.", + "-I$(BINDIR)/.", + ], linkstatic = True, srcs = ["common.c"] + select({ "//build/bazel/platforms/arch:arm": [ @@ -826,30 +971,33 @@ cc_library_static { ], }), )`}, + }) +} + +func TestCcLibrarySTaticArchMultilibSrcsExcludeSrcs(t *testing.T) { + runCcLibraryStaticTestCase(t, bp2buildTestCase{ + description: "cc_library_static arch and multilib srcs and exclude_srcs", + moduleTypeUnderTest: "cc_library_static", + moduleTypeUnderTestFactory: cc.LibraryStaticFactory, + moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build, + depsMutators: []android.RegisterMutatorFunc{cc.RegisterDepsBp2Build}, + filesystem: map[string]string{ + "common.c": "", + "for-arm.c": "", + "for-arm64.c": "", + "for-x86.c": "", + "for-x86_64.c": "", + "for-lib32.c": "", + "for-lib64.c": "", + "not-for-arm.c": "", + "not-for-arm64.c": "", + "not-for-x86.c": "", + "not-for-x86_64.c": "", + "not-for-lib32.c": "", + "not-for-lib64.c": "", + "not-for-everything.c": "", }, - { - description: "cc_library_static arch and multilib srcs and exclude_srcs", - moduleTypeUnderTest: "cc_library_static", - moduleTypeUnderTestFactory: cc.LibraryStaticFactory, - moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build, - depsMutators: []android.RegisterMutatorFunc{cc.RegisterDepsBp2Build}, - filesystem: map[string]string{ - "common.c": "", - "for-arm.c": "", - "for-arm64.c": "", - "for-x86.c": "", - "for-x86_64.c": "", - "for-lib32.c": "", - "for-lib64.c": "", - "not-for-arm.c": "", - "not-for-arm64.c": "", - "not-for-x86.c": "", - "not-for-x86_64.c": "", - "not-for-lib32.c": "", - "not-for-lib64.c": "", - "not-for-everything.c": "", - }, - bp: soongCcLibraryStaticPreamble + ` + blueprint: soongCcLibraryStaticPreamble + ` cc_library_static { name: "foo_static3", srcs: ["common.c", "not-for-*.c"], @@ -865,9 +1013,12 @@ cc_library_static { lib64: { srcs: ["for-lib64.c"], exclude_srcs: ["not-for-lib64.c"] }, }, }`, - expectedBazelTargets: []string{`cc_library_static( + expectedBazelTargets: []string{`cc_library_static( name = "foo_static3", - copts = ["-I."], + copts = [ + "-I.", + "-I$(BINDIR)/.", + ], linkstatic = True, srcs = ["common.c"] + select({ "//build/bazel/platforms/arch:arm": [ @@ -912,64 +1063,96 @@ cc_library_static { ], }), )`}, - }, - } + }) +} - dir := "." - for _, testCase := range testCases { - filesystem := make(map[string][]byte) - toParse := []string{ - "Android.bp", - } - for f, content := range testCase.filesystem { - if strings.HasSuffix(f, "Android.bp") { - toParse = append(toParse, f) - } - filesystem[f] = []byte(content) - } - config := android.TestConfig(buildDir, nil, testCase.bp, filesystem) - ctx := android.NewTestContext(config) +func TestCcLibraryStaticArchSrcsExcludeSrcsGeneratedFiles(t *testing.T) { + runCcLibraryStaticTestCase(t, bp2buildTestCase{ + description: "cc_library_static arch srcs/exclude_srcs with generated files", + moduleTypeUnderTest: "cc_library_static", + moduleTypeUnderTestFactory: cc.LibraryStaticFactory, + moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build, + depsMutators: []android.RegisterMutatorFunc{cc.RegisterDepsBp2Build}, + filesystem: map[string]string{ + "common.c": "", + "for-x86.c": "", + "not-for-x86.c": "", + "not-for-everything.c": "", + "dep/Android.bp": ` +genrule { + name: "generated_src_other_pkg", + out: ["generated_src_other_pkg.cpp"], + cmd: "nothing to see here", +} - cc.RegisterCCBuildComponents(ctx) - ctx.RegisterModuleType("toolchain_library", cc.ToolchainLibraryFactory) - ctx.RegisterModuleType("cc_library_headers", cc.LibraryHeaderFactory) +genrule { + name: "generated_hdr_other_pkg", + out: ["generated_hdr_other_pkg.cpp"], + cmd: "nothing to see here", +} - ctx.RegisterModuleType(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestFactory) - for _, m := range testCase.depsMutators { - ctx.DepsBp2BuildMutators(m) - } - ctx.RegisterBp2BuildMutator(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestBp2BuildMutator) - ctx.RegisterBp2BuildConfig(bp2buildConfig) - ctx.RegisterForBazelConversion() +genrule { + name: "generated_hdr_other_pkg_x86", + out: ["generated_hdr_other_pkg_x86.cpp"], + cmd: "nothing to see here", +}`, + }, + blueprint: soongCcLibraryStaticPreamble + ` +genrule { + name: "generated_src", + out: ["generated_src.cpp"], + cmd: "nothing to see here", +} - _, errs := ctx.ParseFileList(dir, toParse) - if Errored(t, testCase.description, errs) { - continue - } - _, errs = ctx.ResolveDependencies(config) - if Errored(t, testCase.description, errs) { - continue - } +genrule { + name: "generated_src_x86", + out: ["generated_src_x86.cpp"], + cmd: "nothing to see here", +} - checkDir := dir - if testCase.dir != "" { - checkDir = testCase.dir - } - codegenCtx := NewCodegenContext(config, *ctx.Context, Bp2Build) - bazelTargets := generateBazelTargetsForDir(codegenCtx, checkDir) - if actualCount, expectedCount := len(bazelTargets), len(testCase.expectedBazelTargets); actualCount != expectedCount { - t.Errorf("%s: Expected %d bazel target, got %d", testCase.description, expectedCount, actualCount) - } else { - for i, target := range bazelTargets { - if w, g := testCase.expectedBazelTargets[i], target.content; w != g { - t.Errorf( - "%s: Expected generated Bazel target to be '%s', got '%s'", - testCase.description, - w, - g, - ) - } - } - } - } +genrule { + name: "generated_hdr", + out: ["generated_hdr.h"], + cmd: "nothing to see here", +} + +cc_library_static { + name: "foo_static3", + srcs: ["common.c", "not-for-*.c"], + exclude_srcs: ["not-for-everything.c"], + generated_sources: ["generated_src", "generated_src_other_pkg"], + generated_headers: ["generated_hdr", "generated_hdr_other_pkg"], + arch: { + x86: { + srcs: ["for-x86.c"], + exclude_srcs: ["not-for-x86.c"], + generated_sources: ["generated_src_x86"], + generated_headers: ["generated_hdr_other_pkg_x86"], + }, + }, +} +`, + expectedBazelTargets: []string{`cc_library_static( + name = "foo_static3", + copts = [ + "-I.", + "-I$(BINDIR)/.", + ], + linkstatic = True, + srcs = [ + "//dep:generated_hdr_other_pkg", + "//dep:generated_src_other_pkg", + ":generated_hdr", + ":generated_src", + "common.c", + ] + select({ + "//build/bazel/platforms/arch:x86": [ + "//dep:generated_hdr_other_pkg_x86", + ":generated_src_x86", + "for-x86.c", + ], + "//conditions:default": ["not-for-x86.c"], + }), +)`}, + }) } diff --git a/bp2build/cc_object_conversion_test.go b/bp2build/cc_object_conversion_test.go index 9efdb53de..b69135b6e 100644 --- a/bp2build/cc_object_conversion_test.go +++ b/bp2build/cc_object_conversion_test.go @@ -15,35 +15,34 @@ package bp2build import ( + "testing" + "android/soong/android" "android/soong/cc" - "fmt" - "strings" - "testing" ) -func TestCcObjectBp2Build(t *testing.T) { - testCases := []struct { - description string - moduleTypeUnderTest string - moduleTypeUnderTestFactory android.ModuleFactory - moduleTypeUnderTestBp2BuildMutator func(android.TopDownMutatorContext) - blueprint string - expectedBazelTargets []string - filesystem map[string]string - }{ - { - description: "simple cc_object generates cc_object with include header dep", - moduleTypeUnderTest: "cc_object", - moduleTypeUnderTestFactory: cc.ObjectFactory, - moduleTypeUnderTestBp2BuildMutator: cc.ObjectBp2Build, - filesystem: map[string]string{ - "a/b/foo.h": "", - "a/b/bar.h": "", - "a/b/exclude.c": "", - "a/b/c.c": "", - }, - blueprint: `cc_object { +func registerCcObjectModuleTypes(ctx android.RegistrationContext) { + // Always register cc_defaults module factory + ctx.RegisterModuleType("cc_defaults", func() android.Module { return cc.DefaultsFactory() }) +} + +func runCcObjectTestCase(t *testing.T, tc bp2buildTestCase) { + runBp2BuildTestCase(t, registerCcObjectModuleTypes, tc) +} + +func TestCcObjectSimple(t *testing.T) { + runCcObjectTestCase(t, bp2buildTestCase{ + description: "simple cc_object generates cc_object with include header dep", + moduleTypeUnderTest: "cc_object", + moduleTypeUnderTestFactory: cc.ObjectFactory, + moduleTypeUnderTestBp2BuildMutator: cc.ObjectBp2Build, + filesystem: map[string]string{ + "a/b/foo.h": "", + "a/b/bar.h": "", + "a/b/exclude.c": "", + "a/b/c.c": "", + }, + blueprint: `cc_object { name: "foo", local_include_dirs: ["include"], cflags: [ @@ -57,7 +56,7 @@ func TestCcObjectBp2Build(t *testing.T) { exclude_srcs: ["a/b/exclude.c"], } `, - expectedBazelTargets: []string{`cc_object( + expectedBazelTargets: []string{`cc_object( name = "foo", copts = [ "-fno-addrsig", @@ -65,18 +64,23 @@ func TestCcObjectBp2Build(t *testing.T) { "-Wall", "-Werror", "-Iinclude", + "-I$(BINDIR)/include", "-I.", + "-I$(BINDIR)/.", ], srcs = ["a/b/c.c"], )`, - }, }, - { - description: "simple cc_object with defaults", - moduleTypeUnderTest: "cc_object", - moduleTypeUnderTestFactory: cc.ObjectFactory, - moduleTypeUnderTestBp2BuildMutator: cc.ObjectBp2Build, - blueprint: `cc_object { + }) +} + +func TestCcObjectDefaults(t *testing.T) { + runCcObjectTestCase(t, bp2buildTestCase{ + description: "simple cc_object with defaults", + moduleTypeUnderTest: "cc_object", + moduleTypeUnderTestFactory: cc.ObjectFactory, + moduleTypeUnderTestBp2BuildMutator: cc.ObjectBp2Build, + blueprint: `cc_object { name: "foo", local_include_dirs: ["include"], srcs: [ @@ -101,7 +105,7 @@ cc_defaults { ], } `, - expectedBazelTargets: []string{`cc_object( + expectedBazelTargets: []string{`cc_object( name = "foo", copts = [ "-Wno-gcc-compat", @@ -109,22 +113,26 @@ cc_defaults { "-Werror", "-fno-addrsig", "-Iinclude", + "-I$(BINDIR)/include", "-I.", + "-I$(BINDIR)/.", ], srcs = ["a/b/c.c"], )`, - }, + }}) +} + +func TestCcObjectCcObjetDepsInObjs(t *testing.T) { + runCcObjectTestCase(t, bp2buildTestCase{ + description: "cc_object with cc_object deps in objs props", + moduleTypeUnderTest: "cc_object", + moduleTypeUnderTestFactory: cc.ObjectFactory, + moduleTypeUnderTestBp2BuildMutator: cc.ObjectBp2Build, + filesystem: map[string]string{ + "a/b/c.c": "", + "x/y/z.c": "", }, - { - description: "cc_object with cc_object deps in objs props", - moduleTypeUnderTest: "cc_object", - moduleTypeUnderTestFactory: cc.ObjectFactory, - moduleTypeUnderTestBp2BuildMutator: cc.ObjectBp2Build, - filesystem: map[string]string{ - "a/b/c.c": "", - "x/y/z.c": "", - }, - blueprint: `cc_object { + blueprint: `cc_object { name: "foo", srcs: ["a/b/c.c"], objs: ["bar"], @@ -135,11 +143,12 @@ cc_object { srcs: ["x/y/z.c"], } `, - expectedBazelTargets: []string{`cc_object( + expectedBazelTargets: []string{`cc_object( name = "bar", copts = [ "-fno-addrsig", "-I.", + "-I$(BINDIR)/.", ], srcs = ["x/y/z.c"], )`, `cc_object( @@ -147,40 +156,47 @@ cc_object { copts = [ "-fno-addrsig", "-I.", + "-I$(BINDIR)/.", ], deps = [":bar"], srcs = ["a/b/c.c"], )`, - }, }, - { - description: "cc_object with include_build_dir: false", - moduleTypeUnderTest: "cc_object", - moduleTypeUnderTestFactory: cc.ObjectFactory, - moduleTypeUnderTestBp2BuildMutator: cc.ObjectBp2Build, - filesystem: map[string]string{ - "a/b/c.c": "", - "x/y/z.c": "", - }, - blueprint: `cc_object { + }) +} + +func TestCcObjectIncludeBuildDirFalse(t *testing.T) { + runCcObjectTestCase(t, bp2buildTestCase{ + description: "cc_object with include_build_dir: false", + moduleTypeUnderTest: "cc_object", + moduleTypeUnderTestFactory: cc.ObjectFactory, + moduleTypeUnderTestBp2BuildMutator: cc.ObjectBp2Build, + filesystem: map[string]string{ + "a/b/c.c": "", + "x/y/z.c": "", + }, + blueprint: `cc_object { name: "foo", srcs: ["a/b/c.c"], include_build_directory: false, } `, - expectedBazelTargets: []string{`cc_object( + expectedBazelTargets: []string{`cc_object( name = "foo", copts = ["-fno-addrsig"], srcs = ["a/b/c.c"], )`, - }, }, - { - description: "cc_object with product variable", - moduleTypeUnderTest: "cc_object", - moduleTypeUnderTestFactory: cc.ObjectFactory, - moduleTypeUnderTestBp2BuildMutator: cc.ObjectBp2Build, - blueprint: `cc_object { + }) +} + +func TestCcObjectProductVariable(t *testing.T) { + runCcObjectTestCase(t, bp2buildTestCase{ + description: "cc_object with product variable", + moduleTypeUnderTest: "cc_object", + moduleTypeUnderTestFactory: cc.ObjectFactory, + moduleTypeUnderTestBp2BuildMutator: cc.ObjectBp2Build, + blueprint: `cc_object { name: "foo", include_build_directory: false, product_variables: { @@ -190,82 +206,22 @@ cc_object { }, } `, - expectedBazelTargets: []string{`cc_object( + expectedBazelTargets: []string{`cc_object( name = "foo", asflags = ["-DPLATFORM_SDK_VERSION={Platform_sdk_version}"], copts = ["-fno-addrsig"], )`, - }, }, - } - - dir := "." - for _, testCase := range testCases { - filesystem := make(map[string][]byte) - toParse := []string{ - "Android.bp", - } - for f, content := range testCase.filesystem { - if strings.HasSuffix(f, "Android.bp") { - toParse = append(toParse, f) - } - filesystem[f] = []byte(content) - } - config := android.TestConfig(buildDir, nil, testCase.blueprint, filesystem) - ctx := android.NewTestContext(config) - // Always register cc_defaults module factory - ctx.RegisterModuleType("cc_defaults", func() android.Module { return cc.DefaultsFactory() }) - - ctx.RegisterModuleType(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestFactory) - ctx.RegisterBp2BuildMutator(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestBp2BuildMutator) - ctx.RegisterBp2BuildConfig(bp2buildConfig) - ctx.RegisterForBazelConversion() - - _, errs := ctx.ParseFileList(dir, toParse) - if Errored(t, testCase.description, errs) { - continue - } - _, errs = ctx.ResolveDependencies(config) - if Errored(t, testCase.description, errs) { - continue - } - - codegenCtx := NewCodegenContext(config, *ctx.Context, Bp2Build) - bazelTargets := generateBazelTargetsForDir(codegenCtx, dir) - if actualCount, expectedCount := len(bazelTargets), len(testCase.expectedBazelTargets); actualCount != expectedCount { - fmt.Println(bazelTargets) - t.Errorf("%s: Expected %d bazel target, got %d", testCase.description, expectedCount, actualCount) - } else { - for i, target := range bazelTargets { - if w, g := testCase.expectedBazelTargets[i], target.content; w != g { - t.Errorf( - "%s: Expected generated Bazel target to be '%s', got '%s'", - testCase.description, - w, - g, - ) - } - } - } - } + }) } -func TestCcObjectConfigurableAttributesBp2Build(t *testing.T) { - testCases := []struct { - description string - moduleTypeUnderTest string - moduleTypeUnderTestFactory android.ModuleFactory - moduleTypeUnderTestBp2BuildMutator func(android.TopDownMutatorContext) - blueprint string - expectedBazelTargets []string - filesystem map[string]string - }{ - { - description: "cc_object setting cflags for one arch", - moduleTypeUnderTest: "cc_object", - moduleTypeUnderTestFactory: cc.ObjectFactory, - moduleTypeUnderTestBp2BuildMutator: cc.ObjectBp2Build, - blueprint: `cc_object { +func TestCcObjectCflagsOneArch(t *testing.T) { + runCcObjectTestCase(t, bp2buildTestCase{ + description: "cc_object setting cflags for one arch", + moduleTypeUnderTest: "cc_object", + moduleTypeUnderTestFactory: cc.ObjectFactory, + moduleTypeUnderTestBp2BuildMutator: cc.ObjectBp2Build, + blueprint: `cc_object { name: "foo", srcs: ["a.cpp"], arch: { @@ -278,12 +234,13 @@ func TestCcObjectConfigurableAttributesBp2Build(t *testing.T) { }, } `, - expectedBazelTargets: []string{ - `cc_object( + expectedBazelTargets: []string{ + `cc_object( name = "foo", copts = [ "-fno-addrsig", "-I.", + "-I$(BINDIR)/.", ] + select({ "//build/bazel/platforms/arch:x86": ["-fPIC"], "//conditions:default": [], @@ -293,14 +250,17 @@ func TestCcObjectConfigurableAttributesBp2Build(t *testing.T) { "//conditions:default": [], }), )`, - }, }, - { - description: "cc_object setting cflags for 4 architectures", - moduleTypeUnderTest: "cc_object", - moduleTypeUnderTestFactory: cc.ObjectFactory, - moduleTypeUnderTestBp2BuildMutator: cc.ObjectBp2Build, - blueprint: `cc_object { + }) +} + +func TestCcObjectCflagsFourArch(t *testing.T) { + runCcObjectTestCase(t, bp2buildTestCase{ + description: "cc_object setting cflags for 4 architectures", + moduleTypeUnderTest: "cc_object", + moduleTypeUnderTestFactory: cc.ObjectFactory, + moduleTypeUnderTestBp2BuildMutator: cc.ObjectBp2Build, + blueprint: `cc_object { name: "foo", srcs: ["base.cpp"], arch: { @@ -323,12 +283,13 @@ func TestCcObjectConfigurableAttributesBp2Build(t *testing.T) { }, } `, - expectedBazelTargets: []string{ - `cc_object( + expectedBazelTargets: []string{ + `cc_object( name = "foo", copts = [ "-fno-addrsig", "-I.", + "-I$(BINDIR)/.", ] + select({ "//build/bazel/platforms/arch:arm": ["-Wall"], "//build/bazel/platforms/arch:arm64": ["-Wall"], @@ -344,14 +305,17 @@ func TestCcObjectConfigurableAttributesBp2Build(t *testing.T) { "//conditions:default": [], }), )`, - }, }, - { - description: "cc_object setting cflags for multiple OSes", - moduleTypeUnderTest: "cc_object", - moduleTypeUnderTestFactory: cc.ObjectFactory, - moduleTypeUnderTestBp2BuildMutator: cc.ObjectBp2Build, - blueprint: `cc_object { + }) +} + +func TestCcObjectCflagsMultiOs(t *testing.T) { + runCcObjectTestCase(t, bp2buildTestCase{ + description: "cc_object setting cflags for multiple OSes", + moduleTypeUnderTest: "cc_object", + moduleTypeUnderTestFactory: cc.ObjectFactory, + moduleTypeUnderTestBp2BuildMutator: cc.ObjectBp2Build, + blueprint: `cc_object { name: "foo", srcs: ["base.cpp"], target: { @@ -367,12 +331,13 @@ func TestCcObjectConfigurableAttributesBp2Build(t *testing.T) { }, } `, - expectedBazelTargets: []string{ - `cc_object( + expectedBazelTargets: []string{ + `cc_object( name = "foo", copts = [ "-fno-addrsig", "-I.", + "-I$(BINDIR)/.", ] + select({ "//build/bazel/platforms/os:android": ["-fPIC"], "//build/bazel/platforms/os:darwin": ["-Wall"], @@ -381,51 +346,6 @@ func TestCcObjectConfigurableAttributesBp2Build(t *testing.T) { }), srcs = ["base.cpp"], )`, - }, }, - } - - dir := "." - for _, testCase := range testCases { - filesystem := make(map[string][]byte) - toParse := []string{ - "Android.bp", - } - config := android.TestConfig(buildDir, nil, testCase.blueprint, filesystem) - ctx := android.NewTestContext(config) - // Always register cc_defaults module factory - ctx.RegisterModuleType("cc_defaults", func() android.Module { return cc.DefaultsFactory() }) - - ctx.RegisterModuleType(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestFactory) - ctx.RegisterBp2BuildMutator(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestBp2BuildMutator) - ctx.RegisterBp2BuildConfig(bp2buildConfig) - ctx.RegisterForBazelConversion() - - _, errs := ctx.ParseFileList(dir, toParse) - if Errored(t, testCase.description, errs) { - continue - } - _, errs = ctx.ResolveDependencies(config) - if Errored(t, testCase.description, errs) { - continue - } - - codegenCtx := NewCodegenContext(config, *ctx.Context, Bp2Build) - bazelTargets := generateBazelTargetsForDir(codegenCtx, dir) - if actualCount, expectedCount := len(bazelTargets), len(testCase.expectedBazelTargets); actualCount != expectedCount { - fmt.Println(bazelTargets) - t.Errorf("%s: Expected %d bazel target, got %d", testCase.description, expectedCount, actualCount) - } else { - for i, target := range bazelTargets { - if w, g := testCase.expectedBazelTargets[i], target.content; w != g { - t.Errorf( - "%s: Expected generated Bazel target to be '%s', got '%s'", - testCase.description, - w, - g, - ) - } - } - } - } + }) } diff --git a/bp2build/configurability.go b/bp2build/configurability.go index 95a2747d6..2b8f6cc2e 100644 --- a/bp2build/configurability.go +++ b/bp2build/configurability.go @@ -31,8 +31,19 @@ func getStringListValues(list bazel.StringListAttribute) (reflect.Value, selects } func getLabelValue(label bazel.LabelAttribute) (reflect.Value, selects, selects) { - value := reflect.ValueOf(label.Value) - return value, nil, nil + var value reflect.Value + var archSelects selects + + if label.HasConfigurableValues() { + archSelects = map[string]reflect.Value{} + for arch, selectKey := range bazel.PlatformArchMap { + archSelects[selectKey] = reflect.ValueOf(label.GetValueForArch(arch)) + } + } else { + value = reflect.ValueOf(label.Value) + } + + return value, archSelects, nil } func getLabelListValues(list bazel.LabelListAttribute) (reflect.Value, selects, selects) { diff --git a/bp2build/constants.go b/bp2build/constants.go index 70f320e12..4870dffff 100644 --- a/bp2build/constants.go +++ b/bp2build/constants.go @@ -19,7 +19,10 @@ var ( // be preferred for use within a Bazel build. // The file name used for automatically generated files. - GeneratedBuildFileName = "BUILD" + GeneratedBuildFileName = "BUILD.bazel" + // The file name used for hand-crafted build targets. + // NOTE: It is okay that this matches GeneratedBuildFileName, since we generate BUILD files in a different directory to source files + // FIXME: Because there are hundreds of existing BUILD.bazel files in the AOSP tree, we should pick another name here, like BUILD.android HandcraftedBuildFileName = "BUILD.bazel" ) diff --git a/bp2build/conversion.go b/bp2build/conversion.go index d67ab3d77..101ad3d04 100644 --- a/bp2build/conversion.go +++ b/bp2build/conversion.go @@ -2,6 +2,7 @@ package bp2build import ( "android/soong/android" + "android/soong/cc/config" "fmt" "reflect" "sort" @@ -16,6 +17,15 @@ type BazelFile struct { Contents string } +func CreateSoongInjectionFiles() []BazelFile { + var files []BazelFile + + files = append(files, newFile("cc_toolchain", "BUILD", "")) // Creates a //cc_toolchain package. + files = append(files, newFile("cc_toolchain", "constants.bzl", config.BazelCcToolchainVars())) + + return files +} + func CreateBazelFiles( ruleShims map[string]RuleShim, buildToTargets map[string]BazelTargets, @@ -49,7 +59,7 @@ func CreateBazelFiles( func createBuildFiles(buildToTargets map[string]BazelTargets, mode CodegenMode) []BazelFile { files := make([]BazelFile, 0, len(buildToTargets)) for _, dir := range android.SortedStringKeys(buildToTargets) { - if mode == Bp2Build && !android.ShouldWriteBuildFileForDir(dir) { + if mode == Bp2Build && android.ShouldKeepExistingBuildFileForDir(dir) { fmt.Printf("[bp2build] Not writing generated BUILD file for dir: '%s'\n", dir) continue } diff --git a/bp2build/conversion_test.go b/bp2build/conversion_test.go index 262a488f3..0931ff7fc 100644 --- a/bp2build/conversion_test.go +++ b/bp2build/conversion_test.go @@ -29,7 +29,7 @@ func TestCreateBazelFiles_QueryView_AddsTopLevelFiles(t *testing.T) { expectedFilePaths := []bazelFilepath{ { dir: "", - basename: "BUILD", + basename: "BUILD.bazel", }, { dir: "", @@ -37,7 +37,7 @@ func TestCreateBazelFiles_QueryView_AddsTopLevelFiles(t *testing.T) { }, { dir: bazelRulesSubDir, - basename: "BUILD", + basename: "BUILD.bazel", }, { dir: bazelRulesSubDir, @@ -69,7 +69,7 @@ func TestCreateBazelFiles_QueryView_AddsTopLevelFiles(t *testing.T) { if actualFile.Dir != expectedFile.dir || actualFile.Basename != expectedFile.basename { t.Errorf("Did not find expected file %s/%s", actualFile.Dir, actualFile.Basename) - } else if actualFile.Basename == "BUILD" || actualFile.Basename == "WORKSPACE" { + } else if actualFile.Basename == "BUILD.bazel" || actualFile.Basename == "WORKSPACE" { if actualFile.Contents != "" { t.Errorf("Expected %s to have no content.", actualFile) } @@ -79,9 +79,33 @@ func TestCreateBazelFiles_QueryView_AddsTopLevelFiles(t *testing.T) { } } -func TestCreateBazelFiles_Bp2Build_CreatesNoFilesWithNoTargets(t *testing.T) { - files := CreateBazelFiles(map[string]RuleShim{}, map[string]BazelTargets{}, Bp2Build) - if len(files) != 0 { - t.Errorf("Expected no files, got %d", len(files)) +func TestCreateBazelFiles_Bp2Build_CreatesDefaultFiles(t *testing.T) { + files := CreateSoongInjectionFiles() + + expectedFilePaths := []bazelFilepath{ + { + dir: "cc_toolchain", + basename: "BUILD", + }, + { + dir: "cc_toolchain", + basename: "constants.bzl", + }, + } + + if len(files) != len(expectedFilePaths) { + t.Errorf("Expected %d file, got %d", len(expectedFilePaths), len(files)) + } + + for i := range files { + actualFile, expectedFile := files[i], expectedFilePaths[i] + + if actualFile.Dir != expectedFile.dir || actualFile.Basename != expectedFile.basename { + t.Errorf("Did not find expected file %s/%s", actualFile.Dir, actualFile.Basename) + } + + if expectedFile.basename != "BUILD" && actualFile.Contents == "" { + t.Errorf("Contents of %s unexpected empty.", actualFile) + } } } diff --git a/bp2build/python_binary_conversion_test.go b/bp2build/python_binary_conversion_test.go index 2054e0678..95bce3c18 100644 --- a/bp2build/python_binary_conversion_test.go +++ b/bp2build/python_binary_conversion_test.go @@ -1,36 +1,30 @@ package bp2build import ( + "testing" + "android/soong/android" "android/soong/python" - "fmt" - "strings" - "testing" ) -func TestPythonBinaryHost(t *testing.T) { - testCases := []struct { - description string - moduleTypeUnderTest string - moduleTypeUnderTestFactory android.ModuleFactory - moduleTypeUnderTestBp2BuildMutator func(android.TopDownMutatorContext) - blueprint string - expectedBazelTargets []string - filesystem map[string]string - }{ - { - description: "simple python_binary_host converts to a native py_binary", - moduleTypeUnderTest: "python_binary_host", - moduleTypeUnderTestFactory: python.PythonBinaryHostFactory, - moduleTypeUnderTestBp2BuildMutator: python.PythonBinaryBp2Build, - filesystem: map[string]string{ - "a.py": "", - "b/c.py": "", - "b/d.py": "", - "b/e.py": "", - "files/data.txt": "", - }, - blueprint: `python_binary_host { +func runPythonTestCase(t *testing.T, tc bp2buildTestCase) { + runBp2BuildTestCase(t, func(ctx android.RegistrationContext) {}, tc) +} + +func TestPythonBinaryHostSimple(t *testing.T) { + runPythonTestCase(t, bp2buildTestCase{ + description: "simple python_binary_host converts to a native py_binary", + moduleTypeUnderTest: "python_binary_host", + moduleTypeUnderTestFactory: python.PythonBinaryHostFactory, + moduleTypeUnderTestBp2BuildMutator: python.PythonBinaryBp2Build, + filesystem: map[string]string{ + "a.py": "", + "b/c.py": "", + "b/d.py": "", + "b/e.py": "", + "files/data.txt": "", + }, + blueprint: `python_binary_host { name: "foo", main: "a.py", srcs: ["**/*.py"], @@ -39,7 +33,7 @@ func TestPythonBinaryHost(t *testing.T) { bazel_module: { bp2build_available: true }, } `, - expectedBazelTargets: []string{`py_binary( + expectedBazelTargets: []string{`py_binary( name = "foo", data = ["files/data.txt"], main = "a.py", @@ -49,14 +43,17 @@ func TestPythonBinaryHost(t *testing.T) { "b/d.py", ], )`, - }, }, - { - description: "py2 python_binary_host", - moduleTypeUnderTest: "python_binary_host", - moduleTypeUnderTestFactory: python.PythonBinaryHostFactory, - moduleTypeUnderTestBp2BuildMutator: python.PythonBinaryBp2Build, - blueprint: `python_binary_host { + }) +} + +func TestPythonBinaryHostPy2(t *testing.T) { + runPythonTestCase(t, bp2buildTestCase{ + description: "py2 python_binary_host", + moduleTypeUnderTest: "python_binary_host", + moduleTypeUnderTestFactory: python.PythonBinaryHostFactory, + moduleTypeUnderTestBp2BuildMutator: python.PythonBinaryBp2Build, + blueprint: `python_binary_host { name: "foo", srcs: ["a.py"], version: { @@ -71,19 +68,22 @@ func TestPythonBinaryHost(t *testing.T) { bazel_module: { bp2build_available: true }, } `, - expectedBazelTargets: []string{`py_binary( + expectedBazelTargets: []string{`py_binary( name = "foo", python_version = "PY2", srcs = ["a.py"], )`, - }, }, - { - description: "py3 python_binary_host", - moduleTypeUnderTest: "python_binary_host", - moduleTypeUnderTestFactory: python.PythonBinaryHostFactory, - moduleTypeUnderTestBp2BuildMutator: python.PythonBinaryBp2Build, - blueprint: `python_binary_host { + }) +} + +func TestPythonBinaryHostPy3(t *testing.T) { + runPythonTestCase(t, bp2buildTestCase{ + description: "py3 python_binary_host", + moduleTypeUnderTest: "python_binary_host", + moduleTypeUnderTestFactory: python.PythonBinaryHostFactory, + moduleTypeUnderTestBp2BuildMutator: python.PythonBinaryBp2Build, + blueprint: `python_binary_host { name: "foo", srcs: ["a.py"], version: { @@ -98,60 +98,12 @@ func TestPythonBinaryHost(t *testing.T) { bazel_module: { bp2build_available: true }, } `, - expectedBazelTargets: []string{ - // python_version is PY3 by default. - `py_binary( + expectedBazelTargets: []string{ + // python_version is PY3 by default. + `py_binary( name = "foo", srcs = ["a.py"], )`, - }, }, - } - - dir := "." - for _, testCase := range testCases { - filesystem := make(map[string][]byte) - toParse := []string{ - "Android.bp", - } - for f, content := range testCase.filesystem { - if strings.HasSuffix(f, "Android.bp") { - toParse = append(toParse, f) - } - filesystem[f] = []byte(content) - } - config := android.TestConfig(buildDir, nil, testCase.blueprint, filesystem) - ctx := android.NewTestContext(config) - - ctx.RegisterModuleType(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestFactory) - ctx.RegisterBp2BuildMutator(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestBp2BuildMutator) - ctx.RegisterForBazelConversion() - - _, errs := ctx.ParseFileList(dir, toParse) - if Errored(t, testCase.description, errs) { - continue - } - _, errs = ctx.ResolveDependencies(config) - if Errored(t, testCase.description, errs) { - continue - } - - codegenCtx := NewCodegenContext(config, *ctx.Context, Bp2Build) - bazelTargets := generateBazelTargetsForDir(codegenCtx, dir) - if actualCount, expectedCount := len(bazelTargets), len(testCase.expectedBazelTargets); actualCount != expectedCount { - fmt.Println(bazelTargets) - t.Errorf("%s: Expected %d bazel target, got %d", testCase.description, expectedCount, actualCount) - } else { - for i, target := range bazelTargets { - if w, g := testCase.expectedBazelTargets[i], target.content; w != g { - t.Errorf( - "%s: Expected generated Bazel target to be '%s', got '%s'", - testCase.description, - w, - g, - ) - } - } - } - } + }) } diff --git a/bp2build/sh_conversion_test.go b/bp2build/sh_conversion_test.go index 37f542ef7..91bba541d 100644 --- a/bp2build/sh_conversion_test.go +++ b/bp2build/sh_conversion_test.go @@ -15,10 +15,10 @@ package bp2build import ( + "testing" + "android/soong/android" "android/soong/sh" - "strings" - "testing" ) func TestShBinaryLoadStatement(t *testing.T) { @@ -46,88 +46,26 @@ func TestShBinaryLoadStatement(t *testing.T) { t.Fatalf("Expected load statements to be %s, got %s", expected, actual) } } +} +func runShBinaryTestCase(t *testing.T, tc bp2buildTestCase) { + runBp2BuildTestCase(t, func(ctx android.RegistrationContext) {}, tc) } -func TestShBinaryBp2Build(t *testing.T) { - testCases := []struct { - description string - moduleTypeUnderTest string - moduleTypeUnderTestFactory android.ModuleFactory - moduleTypeUnderTestBp2BuildMutator func(android.TopDownMutatorContext) - preArchMutators []android.RegisterMutatorFunc - depsMutators []android.RegisterMutatorFunc - bp string - expectedBazelTargets []string - filesystem map[string]string - dir string - }{ - { - description: "sh_binary test", - moduleTypeUnderTest: "sh_binary", - moduleTypeUnderTestFactory: sh.ShBinaryFactory, - moduleTypeUnderTestBp2BuildMutator: sh.ShBinaryBp2Build, - bp: `sh_binary { +func TestShBinarySimple(t *testing.T) { + runShBinaryTestCase(t, bp2buildTestCase{ + description: "sh_binary test", + moduleTypeUnderTest: "sh_binary", + moduleTypeUnderTestFactory: sh.ShBinaryFactory, + moduleTypeUnderTestBp2BuildMutator: sh.ShBinaryBp2Build, + blueprint: `sh_binary { name: "foo", src: "foo.sh", bazel_module: { bp2build_available: true }, }`, - expectedBazelTargets: []string{`sh_binary( + expectedBazelTargets: []string{`sh_binary( name = "foo", srcs = ["foo.sh"], )`}, - }, - } - - dir := "." - for _, testCase := range testCases { - filesystem := make(map[string][]byte) - toParse := []string{ - "Android.bp", - } - for f, content := range testCase.filesystem { - if strings.HasSuffix(f, "Android.bp") { - toParse = append(toParse, f) - } - filesystem[f] = []byte(content) - } - config := android.TestConfig(buildDir, nil, testCase.bp, filesystem) - ctx := android.NewTestContext(config) - ctx.RegisterModuleType(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestFactory) - for _, m := range testCase.depsMutators { - ctx.DepsBp2BuildMutators(m) - } - ctx.RegisterBp2BuildMutator(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestBp2BuildMutator) - ctx.RegisterForBazelConversion() - - _, errs := ctx.ParseFileList(dir, toParse) - if Errored(t, testCase.description, errs) { - continue - } - _, errs = ctx.ResolveDependencies(config) - if Errored(t, testCase.description, errs) { - continue - } - - checkDir := dir - if testCase.dir != "" { - checkDir = testCase.dir - } - codegenCtx := NewCodegenContext(config, *ctx.Context, Bp2Build) - bazelTargets := generateBazelTargetsForDir(codegenCtx, checkDir) - if actualCount, expectedCount := len(bazelTargets), len(testCase.expectedBazelTargets); actualCount != expectedCount { - t.Errorf("%s: Expected %d bazel target, got %d", testCase.description, expectedCount, actualCount) - } else { - for i, target := range bazelTargets { - if w, g := testCase.expectedBazelTargets[i], target.content; w != g { - t.Errorf( - "%s: Expected generated Bazel target to be '%s', got '%s'", - testCase.description, - w, - g, - ) - } - } - } - } + }) } diff --git a/bp2build/testing.go b/bp2build/testing.go index b925682c4..e575bc6ff 100644 --- a/bp2build/testing.go +++ b/bp2build/testing.go @@ -1,6 +1,8 @@ package bp2build import ( + "testing" + "android/soong/android" "android/soong/bazel" ) @@ -10,6 +12,8 @@ var ( bp2buildConfig = android.Bp2BuildConfig{ android.BP2BUILD_TOPLEVEL: android.Bp2BuildDefaultTrueRecursively, } + + buildDir string ) type nestedProps struct { @@ -39,6 +43,17 @@ type customModule struct { props customProps } +func errored(t *testing.T, desc string, errs []error) bool { + t.Helper() + if len(errs) > 0 { + for _, err := range errs { + t.Errorf("%s: %s", desc, err) + } + return true + } + return false +} + // OutputFiles is needed because some instances of this module use dist with a // tag property which requires the module implements OutputFileProducer. func (m *customModule) OutputFiles(tag string) (android.Paths, error) { diff --git a/build_kzip.bash b/build_kzip.bash index a09335ee0..56550677f 100755 --- a/build_kzip.bash +++ b/build_kzip.bash @@ -6,6 +6,8 @@ # The following environment variables affect the result: # BUILD_NUMBER build number, used to generate unique ID (will use UUID if not set) # SUPERPROJECT_SHA superproject sha, used to generate unique id (will use BUILD_NUMBER if not set) +# SUPERPROJECT_REVISION superproject revision, used for unique id if defined as a sha +# KZIP_NAME name of the output file (will use SUPERPROJECT_REVISION|SUPERPROJECT_SHA|BUILD_NUMBER|UUID if not set) # DIST_DIR where the resulting all.kzip will be placed # KYTHE_KZIP_ENCODING proto or json (proto is default) # KYTHE_JAVA_SOURCE_BATCH_SIZE maximum number of the Java source files in a compilation unit @@ -14,8 +16,16 @@ # TARGET_PRODUCT target device name, e.g., 'aosp_blueline' # XREF_CORPUS source code repository URI, e.g., 'android.googlesource.com/platform/superproject' -: ${BUILD_NUMBER:=$(uuidgen)} -: ${SUPERPROJECT_SHA:=$BUILD_NUMBER} +# If the SUPERPROJECT_REVISION is defined as a sha, use this as the default value if no +# SUPERPROJECT_SHA is specified. +if [[ ${SUPERPROJECT_REVISION:-} =~ [0-9a-f]{40} ]]; then + : ${KZIP_NAME:=${SUPERPROJECT_REVISION:-}} +fi + +: ${KZIP_NAME:=${SUPERPROJECT_SHA:-}} +: ${KZIP_NAME:=${BUILD_NUMBER:-}} +: ${KZIP_NAME:=$(uuidgen)} + : ${KYTHE_JAVA_SOURCE_BATCH_SIZE:=500} : ${KYTHE_KZIP_ENCODING:=proto} : ${XREF_CORPUS:?should be set} @@ -50,6 +60,6 @@ declare -r kzip_count=$(find "$out" -name '*.kzip' | wc -l) # Pack # TODO(asmundak): this should be done by soong. -declare -r allkzip="$SUPERPROJECT_SHA.kzip" +declare -r allkzip="$KZIP_NAME.kzip" "$out/soong/host/linux-x86/bin/merge_zips" "$DIST_DIR/$allkzip" @<(find "$out" -name '*.kzip') diff --git a/build_test.bash b/build_test.bash index 3230f2d0a..296a79cd0 100755 --- a/build_test.bash +++ b/build_test.bash @@ -49,6 +49,10 @@ case $(uname) in esac echo +echo "Free disk space:" +df -h + +echo echo "Running Bazel smoke test..." "${TOP}/tools/bazel" --batch --max_idle_secs=1 info diff --git a/cc/Android.bp b/cc/Android.bp index c32cca8b8..1fc8d9f68 100644 --- a/cc/Android.bp +++ b/cc/Android.bp @@ -92,6 +92,7 @@ bootstrap_go_package { "object_test.go", "prebuilt_test.go", "proto_test.go", + "sanitize_test.go", "test_data_test.go", "vendor_public_library_test.go", "vendor_snapshot_test.go", diff --git a/cc/bp2build.go b/cc/bp2build.go index 4c01de562..95a3fe157 100644 --- a/cc/bp2build.go +++ b/cc/bp2build.go @@ -14,10 +14,11 @@ package cc import ( - "android/soong/android" - "android/soong/bazel" "path/filepath" "strings" + + "android/soong/android" + "android/soong/bazel" ) // bp2build functions and helpers for converting cc_* modules to Bazel. @@ -49,7 +50,23 @@ func depsBp2BuildMutator(ctx android.BottomUpMutatorContext) { var allDeps []string - for _, p := range module.GetTargetProperties(&BaseLinkerProperties{}) { + for _, p := range module.GetTargetProperties(ctx, &BaseCompilerProperties{}) { + // base compiler props + if baseCompilerProps, ok := p.(*BaseCompilerProperties); ok { + allDeps = append(allDeps, baseCompilerProps.Generated_headers...) + allDeps = append(allDeps, baseCompilerProps.Generated_sources...) + } + } + + for _, p := range module.GetArchProperties(ctx, &BaseCompilerProperties{}) { + // arch specific compiler props + if baseCompilerProps, ok := p.(*BaseCompilerProperties); ok { + allDeps = append(allDeps, baseCompilerProps.Generated_headers...) + allDeps = append(allDeps, baseCompilerProps.Generated_sources...) + } + } + + for _, p := range module.GetTargetProperties(ctx, &BaseLinkerProperties{}) { // arch specific linker props if baseLinkerProps, ok := p.(*BaseLinkerProperties); ok { allDeps = append(allDeps, baseLinkerProps.Header_libs...) @@ -74,12 +91,15 @@ func depsBp2BuildMutator(ctx android.BottomUpMutatorContext) { allDeps = append(allDeps, lib.SharedProperties.Shared.Static_libs...) allDeps = append(allDeps, lib.SharedProperties.Shared.Whole_static_libs...) allDeps = append(allDeps, lib.SharedProperties.Shared.Shared_libs...) - allDeps = append(allDeps, lib.SharedProperties.Shared.System_shared_libs...) allDeps = append(allDeps, lib.StaticProperties.Static.Static_libs...) allDeps = append(allDeps, lib.StaticProperties.Static.Whole_static_libs...) allDeps = append(allDeps, lib.StaticProperties.Static.Shared_libs...) - allDeps = append(allDeps, lib.StaticProperties.Static.System_shared_libs...) + + // TODO(b/186024507, b/186489250): Temporarily exclude adding + // system_shared_libs deps until libc and libm builds. + // allDeps = append(allDeps, lib.SharedProperties.Shared.System_shared_libs...) + // allDeps = append(allDeps, lib.StaticProperties.Static.System_shared_libs...) } ctx.AddDependency(module, nil, android.SortedUniqueStrings(allDeps)...) @@ -173,11 +193,19 @@ func bp2BuildParseCompilerProps(ctx android.TopDownMutatorContext, module *Modul var srcs bazel.LabelListAttribute var copts bazel.StringListAttribute - // Creates the -I flag for a directory, while making the directory relative + // Creates the -I flags for a directory, while making the directory relative // to the exec root for Bazel to work. - includeFlag := func(dir string) string { + includeFlags := func(dir string) []string { // filepath.Join canonicalizes the path, i.e. it takes care of . or .. elements. - return "-I" + filepath.Join(ctx.ModuleDir(), dir) + moduleDirRootedPath := filepath.Join(ctx.ModuleDir(), dir) + return []string{ + "-I" + moduleDirRootedPath, + // Include the bindir-rooted path (using make variable substitution). This most + // closely matches Bazel's native include path handling, which allows for dependency + // on generated headers in these directories. + // TODO(b/188084383): Handle local include directories in Bazel. + "-I$(BINDIR)/" + moduleDirRootedPath, + } } // Parse the list of module-relative include directories (-I). @@ -190,14 +218,14 @@ func bp2BuildParseCompilerProps(ctx android.TopDownMutatorContext, module *Modul // Parse the list of copts. parseCopts := func(baseCompilerProps *BaseCompilerProperties) []string { var copts []string - for _, flag := range baseCompilerProps.Cflags { + for _, flag := range append(baseCompilerProps.Cflags, baseCompilerProps.Cppflags...) { // Soong's cflags can contain spaces, like `-include header.h`. For // Bazel's copts, split them up to be compatible with the // no_copts_tokenization feature. copts = append(copts, strings.Split(flag, " ")...) } for _, dir := range parseLocalIncludeDirs(baseCompilerProps) { - copts = append(copts, includeFlag(dir)) + copts = append(copts, includeFlags(dir)...) } return copts } @@ -215,9 +243,17 @@ func bp2BuildParseCompilerProps(ctx android.TopDownMutatorContext, module *Modul parseSrcs := func(baseCompilerProps *BaseCompilerProperties) bazel.LabelList { // Combine the base srcs and arch-specific srcs allSrcs := append(baseSrcs, baseCompilerProps.Srcs...) + // Add srcs-like dependencies such as generated files. + // First create a LabelList containing these dependencies, then merge the values with srcs. + generatedHdrsAndSrcs := baseCompilerProps.Generated_headers + generatedHdrsAndSrcs = append(generatedHdrsAndSrcs, baseCompilerProps.Generated_sources...) + + generatedHdrsAndSrcsLabelList := android.BazelLabelForModuleDeps(ctx, generatedHdrsAndSrcs) + // Combine the base exclude_srcs and configuration-specific exclude_srcs allExcludeSrcs := append(baseExcludeSrcs, baseCompilerProps.Exclude_srcs...) - return android.BazelLabelForModuleSrcExcludes(ctx, allSrcs, allExcludeSrcs) + allSrcsLabelList := android.BazelLabelForModuleSrcExcludes(ctx, allSrcs, allExcludeSrcs) + return bazel.AppendBazelLabelLists(allSrcsLabelList, generatedHdrsAndSrcsLabelList) } for _, props := range module.compiler.compilerProps() { @@ -227,8 +263,8 @@ func bp2BuildParseCompilerProps(ctx android.TopDownMutatorContext, module *Modul // Used for arch-specific srcs later. baseSrcs = baseCompilerProps.Srcs - baseExcludeSrcs = baseCompilerProps.Exclude_srcs baseSrcsLabelList = parseSrcs(baseCompilerProps) + baseExcludeSrcs = baseCompilerProps.Exclude_srcs break } } @@ -237,9 +273,9 @@ func bp2BuildParseCompilerProps(ctx android.TopDownMutatorContext, module *Modul // target has access to all headers recursively in the package, and has // "-I<module-dir>" in its copts. if c, ok := module.compiler.(*baseCompiler); ok && c.includeBuildDirectory() { - copts.Value = append(copts.Value, includeFlag(".")) + copts.Value = append(copts.Value, includeFlags(".")...) } else if c, ok := module.compiler.(*libraryDecorator); ok && c.includeBuildDirectory() { - copts.Value = append(copts.Value, includeFlag(".")) + copts.Value = append(copts.Value, includeFlags(".")...) } for arch, props := range module.GetArchProperties(ctx, &BaseCompilerProperties{}) { @@ -273,7 +309,7 @@ func bp2BuildParseCompilerProps(ctx android.TopDownMutatorContext, module *Modul srcs.SetValueForArch(bazel.CONDITIONS_DEFAULT, defaultsSrcs) // Handle OS specific props. - for os, props := range module.GetTargetProperties(&BaseCompilerProperties{}) { + for os, props := range module.GetTargetProperties(ctx, &BaseCompilerProperties{}) { if baseCompilerProps, ok := props.(*BaseCompilerProperties); ok { srcsList := parseSrcs(baseCompilerProps) // TODO(b/186153868): add support for os-specific srcs and exclude_srcs @@ -293,6 +329,7 @@ type linkerAttributes struct { deps bazel.LabelListAttribute dynamicDeps bazel.LabelListAttribute wholeArchiveDeps bazel.LabelListAttribute + exportedDeps bazel.LabelListAttribute linkopts bazel.StringListAttribute versionScript bazel.LabelAttribute } @@ -310,6 +347,7 @@ func getBp2BuildLinkerFlags(linkerProperties *BaseLinkerProperties) []string { // configurable attribute values. func bp2BuildParseLinkerProps(ctx android.TopDownMutatorContext, module *Module) linkerAttributes { var deps bazel.LabelListAttribute + var exportedDeps bazel.LabelListAttribute var dynamicDeps bazel.LabelListAttribute var wholeArchiveDeps bazel.LabelListAttribute var linkopts bazel.StringListAttribute @@ -318,11 +356,12 @@ func bp2BuildParseLinkerProps(ctx android.TopDownMutatorContext, module *Module) for _, linkerProps := range module.linker.linkerProps() { if baseLinkerProps, ok := linkerProps.(*BaseLinkerProperties); ok { libs := baseLinkerProps.Header_libs - libs = append(libs, baseLinkerProps.Export_header_lib_headers...) libs = append(libs, baseLinkerProps.Static_libs...) + exportedLibs := baseLinkerProps.Export_header_lib_headers wholeArchiveLibs := baseLinkerProps.Whole_static_libs libs = android.SortedUniqueStrings(libs) deps = bazel.MakeLabelListAttribute(android.BazelLabelForModuleDeps(ctx, libs)) + exportedDeps = bazel.MakeLabelListAttribute(android.BazelLabelForModuleDeps(ctx, exportedLibs)) linkopts.Value = getBp2BuildLinkerFlags(baseLinkerProps) wholeArchiveDeps = bazel.MakeLabelListAttribute(android.BazelLabelForModuleDeps(ctx, wholeArchiveLibs)) @@ -340,11 +379,12 @@ func bp2BuildParseLinkerProps(ctx android.TopDownMutatorContext, module *Module) for arch, p := range module.GetArchProperties(ctx, &BaseLinkerProperties{}) { if baseLinkerProps, ok := p.(*BaseLinkerProperties); ok { libs := baseLinkerProps.Header_libs - libs = append(libs, baseLinkerProps.Export_header_lib_headers...) libs = append(libs, baseLinkerProps.Static_libs...) + exportedLibs := baseLinkerProps.Export_header_lib_headers wholeArchiveLibs := baseLinkerProps.Whole_static_libs libs = android.SortedUniqueStrings(libs) deps.SetValueForArch(arch.Name, android.BazelLabelForModuleDeps(ctx, libs)) + exportedDeps.SetValueForArch(arch.Name, android.BazelLabelForModuleDeps(ctx, exportedLibs)) linkopts.SetValueForArch(arch.Name, getBp2BuildLinkerFlags(baseLinkerProps)) wholeArchiveDeps.SetValueForArch(arch.Name, android.BazelLabelForModuleDeps(ctx, wholeArchiveLibs)) @@ -358,15 +398,16 @@ func bp2BuildParseLinkerProps(ctx android.TopDownMutatorContext, module *Module) } } - for os, p := range module.GetTargetProperties(&BaseLinkerProperties{}) { + for os, p := range module.GetTargetProperties(ctx, &BaseLinkerProperties{}) { if baseLinkerProps, ok := p.(*BaseLinkerProperties); ok { libs := baseLinkerProps.Header_libs - libs = append(libs, baseLinkerProps.Export_header_lib_headers...) libs = append(libs, baseLinkerProps.Static_libs...) + exportedLibs := baseLinkerProps.Export_header_lib_headers wholeArchiveLibs := baseLinkerProps.Whole_static_libs libs = android.SortedUniqueStrings(libs) wholeArchiveDeps.SetValueForOS(os.Name, android.BazelLabelForModuleDeps(ctx, wholeArchiveLibs)) deps.SetValueForOS(os.Name, android.BazelLabelForModuleDeps(ctx, libs)) + exportedDeps.SetValueForOS(os.Name, android.BazelLabelForModuleDeps(ctx, exportedLibs)) linkopts.SetValueForOS(os.Name, getBp2BuildLinkerFlags(baseLinkerProps)) @@ -377,6 +418,7 @@ func bp2BuildParseLinkerProps(ctx android.TopDownMutatorContext, module *Module) return linkerAttributes{ deps: deps, + exportedDeps: exportedDeps, dynamicDeps: dynamicDeps, wholeArchiveDeps: wholeArchiveDeps, linkopts: linkopts, @@ -434,7 +476,7 @@ func bp2BuildParseExportedIncludes(ctx android.TopDownMutatorContext, module *Mo } } - for os, props := range module.GetTargetProperties(&FlagExporterProperties{}) { + for os, props := range module.GetTargetProperties(ctx, &FlagExporterProperties{}) { if flagExporterProperties, ok := props.(*FlagExporterProperties); ok { osIncludeDirs := flagExporterProperties.Export_system_include_dirs osIncludeDirs = append(osIncludeDirs, flagExporterProperties.Export_include_dirs...) diff --git a/cc/builder.go b/cc/builder.go index 29cde9d2c..51c8a0bdf 100644 --- a/cc/builder.go +++ b/cc/builder.go @@ -804,6 +804,7 @@ func transformObjToDynamicBinary(ctx android.ModuleContext, ImplicitOutputs: implicitOutputs, Inputs: objFiles, Implicits: deps, + OrderOnly: sharedLibs, Args: args, }) } @@ -841,6 +841,10 @@ func (c *Module) SetHideFromMake() { c.Properties.HideFromMake = true } +func (c *Module) HiddenFromMake() bool { + return c.Properties.HideFromMake +} + func (c *Module) Toc() android.OptionalPath { if c.linker != nil { if library, ok := c.linker.(libraryInterface); ok { @@ -1088,12 +1092,6 @@ func (c *Module) IsDependencyRoot() bool { return false } -// Returns true if the module is using VNDK libraries instead of the libraries in /system/lib or /system/lib64. -// "product" and "vendor" variant modules return true for this function. -// When BOARD_VNDK_VERSION is set, vendor variants of "vendor_available: true", "vendor: true", -// "soc_specific: true" and more vendor installed modules are included here. -// When PRODUCT_PRODUCT_VNDK_VERSION is set, product variants of "product_available: true" or -// "product_specific: true" modules are included here. func (c *Module) UseVndk() bool { return c.Properties.VndkVersion != "" } @@ -1141,6 +1139,18 @@ func (c *Module) IsVendorPublicLibrary() bool { return c.VendorProperties.IsVendorPublicLibrary } +func (c *Module) HasLlndkStubs() bool { + lib := moduleLibraryInterface(c) + return lib != nil && lib.hasLLNDKStubs() +} + +func (c *Module) StubsVersion() string { + if lib, ok := c.linker.(versionedInterface); ok { + return lib.stubsVersion() + } + panic(fmt.Errorf("StubsVersion called on non-versioned module: %q", c.BaseModuleName())) +} + // isImplementationForLLNDKPublic returns true for any variant of a cc_library that has LLNDK stubs // and does not set llndk.vendor_available: false. func (c *Module) isImplementationForLLNDKPublic() bool { @@ -1185,7 +1195,7 @@ func (c *Module) isNDKStubLibrary() bool { return false } -func (c *Module) isVndkSp() bool { +func (c *Module) IsVndkSp() bool { if vndkdep := c.vndkdep; vndkdep != nil { return vndkdep.isVndkSp() } @@ -1204,7 +1214,7 @@ func (c *Module) SubName() string { } func (c *Module) MustUseVendorVariant() bool { - return c.isVndkSp() || c.Properties.MustUseVendorVariant + return c.IsVndkSp() || c.Properties.MustUseVendorVariant } func (c *Module) getVndkExtendsModuleName() string { @@ -1343,11 +1353,11 @@ func (ctx *moduleContextImpl) header() bool { } func (ctx *moduleContextImpl) binary() bool { - return ctx.mod.binary() + return ctx.mod.Binary() } func (ctx *moduleContextImpl) object() bool { - return ctx.mod.object() + return ctx.mod.Object() } func (ctx *moduleContextImpl) canUseSdk() bool { @@ -1445,7 +1455,7 @@ func (ctx *moduleContextImpl) isNDKStubLibrary() bool { } func (ctx *moduleContextImpl) isVndkSp() bool { - return ctx.mod.isVndkSp() + return ctx.mod.IsVndkSp() } func (ctx *moduleContextImpl) IsVndkExt() bool { @@ -1786,7 +1796,7 @@ func (c *Module) GenerateAndroidBuildActions(actx android.ModuleContext) { // glob exported headers for snapshot, if BOARD_VNDK_VERSION is current or // RECOVERY_SNAPSHOT_VERSION is current. if i, ok := c.linker.(snapshotLibraryInterface); ok { - if shouldCollectHeadersForSnapshot(ctx, c, apexInfo) { + if ShouldCollectHeadersForSnapshot(ctx, c, apexInfo) { i.collectHeadersForSnapshot(ctx) } } @@ -1799,7 +1809,7 @@ func (c *Module) GenerateAndroidBuildActions(actx android.ModuleContext) { // modules can be hidden from make as some are needed for resolving make side // dependencies. c.HideFromMake() - } else if !c.installable(apexInfo) { + } else if !installable(c, apexInfo) { c.SkipInstall() } @@ -2451,7 +2461,7 @@ func checkDoubleLoadableLibraries(ctx android.TopDownMutatorContext) { return true } - if to.isVndkSp() || to.IsLlndk() { + if to.IsVndkSp() || to.IsLlndk() { return false } @@ -2823,7 +2833,7 @@ func (c *Module) depsToPaths(ctx android.ModuleContext) PathDeps { // Add the dependency to the APEX(es) providing the library so that // m <module> can trigger building the APEXes as well. depApexInfo := ctx.OtherModuleProvider(dep, android.ApexInfoProvider).(android.ApexInfo) - for _, an := range depApexInfo.InApexes { + for _, an := range depApexInfo.InApexVariants { c.Properties.ApexesProvidingSharedLibs = append( c.Properties.ApexesProvidingSharedLibs, an) } @@ -3064,7 +3074,7 @@ func (c *Module) Header() bool { return false } -func (c *Module) binary() bool { +func (c *Module) Binary() bool { if b, ok := c.linker.(interface { binary() bool }); ok { @@ -3073,7 +3083,7 @@ func (c *Module) binary() bool { return false } -func (c *Module) object() bool { +func (c *Module) Object() bool { if o, ok := c.linker.(interface { object() bool }); ok { @@ -3155,18 +3165,25 @@ func (c *Module) UniqueApexVariations() bool { } } -// Return true if the module is ever installable. func (c *Module) EverInstallable() bool { return c.installer != nil && // Check to see whether the module is actually ever installable. c.installer.everInstallable() } -func (c *Module) installable(apexInfo android.ApexInfo) bool { +func (c *Module) PreventInstall() bool { + return c.Properties.PreventInstall +} + +func (c *Module) Installable() *bool { + return c.Properties.Installable +} + +func installable(c LinkableInterface, apexInfo android.ApexInfo) bool { ret := c.EverInstallable() && // Check to see whether the module has been configured to not be installed. - proptools.BoolDefault(c.Properties.Installable, true) && - !c.Properties.PreventInstall && c.outputFile.Valid() + proptools.BoolDefault(c.Installable(), true) && + !c.PreventInstall() && c.OutputFile().Valid() // The platform variant doesn't need further condition. Apex variants however might not // be installable because it will likely to be included in the APEX and won't appear @@ -3213,6 +3230,9 @@ func (c *Module) DepIsInSameApex(ctx android.BaseModuleContext, dep android.Modu return false } } + if cc.IsLlndk() { + return false + } if isLibDepTag && c.static() && libDepTag.shared() { // shared_lib dependency from a static lib is considered as crossing // the APEX boundary because the dependency doesn't actually is @@ -3279,6 +3299,12 @@ func (c *Module) ShouldSupportSdkVersion(ctx android.BaseModuleContext, return nil } +// Implements android.ApexModule +func (c *Module) AlwaysRequiresPlatformApexVariant() bool { + // stub libraries and native bridge libraries are always available to platform + return c.IsStubs() || c.Target().NativeBridge == android.NativeBridgeEnabled +} + // // Defaults // diff --git a/cc/cc_test.go b/cc/cc_test.go index e9daf33fc..d82619a04 100644 --- a/cc/cc_test.go +++ b/cc/cc_test.go @@ -381,8 +381,8 @@ func checkVndkModule(t *testing.T, ctx *android.TestContext, name, subDir string if !mod.IsVndk() { t.Errorf("%q IsVndk() must equal to true", name) } - if mod.isVndkSp() != isVndkSp { - t.Errorf("%q isVndkSp() must equal to %t", name, isVndkSp) + if mod.IsVndkSp() != isVndkSp { + t.Errorf("%q IsVndkSp() must equal to %t", name, isVndkSp) } // Check VNDK extension properties. diff --git a/cc/config/Android.bp b/cc/config/Android.bp index 5ef247df9..e4a8b6203 100644 --- a/cc/config/Android.bp +++ b/cc/config/Android.bp @@ -10,6 +10,7 @@ bootstrap_go_package { "soong-remoteexec", ], srcs: [ + "bp2build.go", "clang.go", "global.go", "tidy.go", @@ -31,6 +32,7 @@ bootstrap_go_package { "arm64_linux_host.go", ], testSrcs: [ + "bp2build_test.go", "tidy_test.go", ], } diff --git a/cc/config/arm_device.go b/cc/config/arm_device.go index a402f8f7a..439084e3b 100644 --- a/cc/config/arm_device.go +++ b/cc/config/arm_device.go @@ -163,56 +163,49 @@ var ( ) const ( + name = "arm" armGccVersion = "4.9" + gccTriple = "arm-linux-androideabi" + clangTriple = "armv7a-linux-androideabi" ) func init() { pctx.StaticVariable("armGccVersion", armGccVersion) - pctx.SourcePathVariable("ArmGccRoot", - "prebuilts/gcc/${HostPrebuiltTag}/arm/arm-linux-androideabi-${armGccVersion}") + pctx.SourcePathVariable("ArmGccRoot", "prebuilts/gcc/${HostPrebuiltTag}/arm/arm-linux-androideabi-${armGccVersion}") - pctx.StaticVariable("ArmLdflags", strings.Join(armLdflags, " ")) - pctx.StaticVariable("ArmLldflags", strings.Join(armLldflags, " ")) + // Just exported. Not created as a Ninja static variable. + exportedStringVars.Set("ArmClangTriple", clangTriple) + + exportStringListStaticVariable("ArmLdflags", armLdflags) + exportStringListStaticVariable("ArmLldflags", armLldflags) // Clang cflags - pctx.StaticVariable("ArmToolchainClangCflags", strings.Join(ClangFilterUnknownCflags(armToolchainCflags), " ")) - pctx.StaticVariable("ArmClangCflags", strings.Join(ClangFilterUnknownCflags(armCflags), " ")) - pctx.StaticVariable("ArmClangLdflags", strings.Join(ClangFilterUnknownCflags(armLdflags), " ")) - pctx.StaticVariable("ArmClangLldflags", strings.Join(ClangFilterUnknownCflags(armLldflags), " ")) - pctx.StaticVariable("ArmClangCppflags", strings.Join(ClangFilterUnknownCflags(armCppflags), " ")) + exportStringListStaticVariable("ArmToolchainClangCflags", ClangFilterUnknownCflags(armToolchainCflags)) + exportStringListStaticVariable("ArmClangCflags", ClangFilterUnknownCflags(armCflags)) + exportStringListStaticVariable("ArmClangLdflags", ClangFilterUnknownCflags(armLdflags)) + exportStringListStaticVariable("ArmClangLldflags", ClangFilterUnknownCflags(armLldflags)) + exportStringListStaticVariable("ArmClangCppflags", ClangFilterUnknownCflags(armCppflags)) // Clang ARM vs. Thumb instruction set cflags - pctx.StaticVariable("ArmClangArmCflags", strings.Join(ClangFilterUnknownCflags(armArmCflags), " ")) - pctx.StaticVariable("ArmClangThumbCflags", strings.Join(ClangFilterUnknownCflags(armThumbCflags), " ")) + exportStringListStaticVariable("ArmClangArmCflags", ClangFilterUnknownCflags(armArmCflags)) + exportStringListStaticVariable("ArmClangThumbCflags", ClangFilterUnknownCflags(armThumbCflags)) // Clang arch variant cflags - pctx.StaticVariable("ArmClangArmv7ACflags", - strings.Join(armClangArchVariantCflags["armv7-a"], " ")) - pctx.StaticVariable("ArmClangArmv7ANeonCflags", - strings.Join(armClangArchVariantCflags["armv7-a-neon"], " ")) - pctx.StaticVariable("ArmClangArmv8ACflags", - strings.Join(armClangArchVariantCflags["armv8-a"], " ")) - pctx.StaticVariable("ArmClangArmv82ACflags", - strings.Join(armClangArchVariantCflags["armv8-2a"], " ")) + exportStringListStaticVariable("ArmClangArmv7ACflags", armClangArchVariantCflags["armv7-a"]) + exportStringListStaticVariable("ArmClangArmv7ANeonCflags", armClangArchVariantCflags["armv7-a-neon"]) + exportStringListStaticVariable("ArmClangArmv8ACflags", armClangArchVariantCflags["armv8-a"]) + exportStringListStaticVariable("ArmClangArmv82ACflags", armClangArchVariantCflags["armv8-2a"]) // Clang cpu variant cflags - pctx.StaticVariable("ArmClangGenericCflags", - strings.Join(armClangCpuVariantCflags[""], " ")) - pctx.StaticVariable("ArmClangCortexA7Cflags", - strings.Join(armClangCpuVariantCflags["cortex-a7"], " ")) - pctx.StaticVariable("ArmClangCortexA8Cflags", - strings.Join(armClangCpuVariantCflags["cortex-a8"], " ")) - pctx.StaticVariable("ArmClangCortexA15Cflags", - strings.Join(armClangCpuVariantCflags["cortex-a15"], " ")) - pctx.StaticVariable("ArmClangCortexA53Cflags", - strings.Join(armClangCpuVariantCflags["cortex-a53"], " ")) - pctx.StaticVariable("ArmClangCortexA55Cflags", - strings.Join(armClangCpuVariantCflags["cortex-a55"], " ")) - pctx.StaticVariable("ArmClangKraitCflags", - strings.Join(armClangCpuVariantCflags["krait"], " ")) - pctx.StaticVariable("ArmClangKryoCflags", - strings.Join(armClangCpuVariantCflags["kryo"], " ")) + exportStringListStaticVariable("ArmClangGenericCflags", armClangCpuVariantCflags[""]) + exportStringListStaticVariable("ArmClangCortexA7Cflags", armClangCpuVariantCflags["cortex-a7"]) + exportStringListStaticVariable("ArmClangCortexA8Cflags", armClangCpuVariantCflags["cortex-a8"]) + exportStringListStaticVariable("ArmClangCortexA15Cflags", armClangCpuVariantCflags["cortex-a15"]) + exportStringListStaticVariable("ArmClangCortexA53Cflags", armClangCpuVariantCflags["cortex-a53"]) + exportStringListStaticVariable("ArmClangCortexA55Cflags", armClangCpuVariantCflags["cortex-a55"]) + exportStringListStaticVariable("ArmClangKraitCflags", armClangCpuVariantCflags["krait"]) + exportStringListStaticVariable("ArmClangKryoCflags", armClangCpuVariantCflags["kryo"]) } var ( @@ -251,7 +244,7 @@ type toolchainArm struct { } func (t *toolchainArm) Name() string { - return "arm" + return name } func (t *toolchainArm) GccRoot() string { @@ -259,7 +252,7 @@ func (t *toolchainArm) GccRoot() string { } func (t *toolchainArm) GccTriple() string { - return "arm-linux-androideabi" + return gccTriple } func (t *toolchainArm) GccVersion() string { @@ -272,7 +265,7 @@ func (t *toolchainArm) IncludeFlags() string { func (t *toolchainArm) ClangTriple() string { // http://b/72619014 work around llvm LTO bug. - return "armv7a-linux-androideabi" + return clangTriple } func (t *toolchainArm) ndkTriple() string { @@ -312,7 +305,7 @@ func (t *toolchainArm) ClangInstructionSetFlags(isa string) (string, error) { } func (toolchainArm) LibclangRuntimeLibraryArch() string { - return "arm" + return name } func armToolchainFactory(arch android.Arch) Toolchain { diff --git a/cc/config/bp2build.go b/cc/config/bp2build.go new file mode 100644 index 000000000..19571f10d --- /dev/null +++ b/cc/config/bp2build.go @@ -0,0 +1,169 @@ +// Copyright 2021 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 config + +import ( + "android/soong/android" + "fmt" + "regexp" + "strings" +) + +// Helpers for exporting cc configuration information to Bazel. +var ( + // Map containing toolchain variables that are independent of the + // environment variables of the build. + exportedStringListVars = exportedStringListVariables{} + exportedStringVars = exportedStringVariables{} +) + +type exportedStringVariables map[string]string +type exportedStringListVariables map[string][]string + +func (m exportedStringVariables) Set(k string, v string) { + m[k] = v +} + +// Convenience function to declare a static variable and export it to Bazel's cc_toolchain. +func exportStringStaticVariable(name string, value string) { + pctx.StaticVariable(name, value) + exportedStringVars.Set(name, value) +} + +func (m exportedStringListVariables) Set(k string, v []string) { + m[k] = v +} + +// Convenience function to declare a static variable and export it to Bazel's cc_toolchain. +func exportStringListStaticVariable(name string, value []string) { + pctx.StaticVariable(name, strings.Join(value, " ")) + exportedStringListVars.Set(name, value) +} + +// BazelCcToolchainVars generates bzl file content containing variables for +// Bazel's cc_toolchain configuration. +func BazelCcToolchainVars() string { + ret := "# GENERATED FOR BAZEL FROM SOONG. DO NOT EDIT.\n\n" + + // Ensure that string s has no invalid characters to be generated into the bzl file. + validateCharacters := func(s string) string { + for _, c := range []string{`\n`, `"`, `\`} { + if strings.Contains(s, c) { + panic(fmt.Errorf("%s contains illegal character %s", s, c)) + } + } + return s + } + + // For each exported variable, recursively expand elements in the variableValue + // list to ensure that interpolated variables are expanded according to their values + // in the variable scope. + for _, k := range android.SortedStringKeys(exportedStringListVars) { + variableValue := exportedStringListVars[k] + var expandedVars []string + for _, v := range variableValue { + expandedVars = append(expandedVars, expandVar(v, exportedStringVars, exportedStringListVars)...) + } + // Build the list for this variable. + list := "[" + for _, flag := range expandedVars { + list += fmt.Sprintf("\n \"%s\",", validateCharacters(flag)) + } + list += "\n]" + // Assign the list as a bzl-private variable; this variable will be exported + // out through a constants struct later. + ret += fmt.Sprintf("_%s = %s\n", k, list) + ret += "\n" + } + + for _, k := range android.SortedStringKeys(exportedStringVars) { + variableValue := exportedStringVars[k] + expandedVar := expandVar(variableValue, exportedStringVars, exportedStringListVars) + if len(expandedVar) > 1 { + panic(fmt.Errorf("%s expands to more than one string value: %s", variableValue, expandedVar)) + } + ret += fmt.Sprintf("_%s = \"%s\"\n", k, validateCharacters(expandedVar[0])) + ret += "\n" + } + + // Build the exported constants struct. + ret += "constants = struct(\n" + for _, k := range android.SortedStringKeys(exportedStringVars) { + ret += fmt.Sprintf(" %s = _%s,\n", k, k) + } + for _, k := range android.SortedStringKeys(exportedStringListVars) { + ret += fmt.Sprintf(" %s = _%s,\n", k, k) + } + ret += ")" + return ret +} + +// expandVar recursively expand interpolated variables in the exportedVars scope. +// +// We're using a string slice to track the seen variables to avoid +// stackoverflow errors with infinite recursion. it's simpler to use a +// string slice than to handle a pass-by-referenced map, which would make it +// quite complex to track depth-first interpolations. It's also unlikely the +// interpolation stacks are deep (n > 1). +func expandVar(toExpand string, stringScope exportedStringVariables, stringListScope exportedStringListVariables) []string { + // e.g. "${ClangExternalCflags}" + r := regexp.MustCompile(`\${([a-zA-Z0-9_]+)}`) + + // Internal recursive function. + var expandVarInternal func(string, map[string]bool) []string + expandVarInternal = func(toExpand string, seenVars map[string]bool) []string { + var ret []string + for _, v := range strings.Split(toExpand, " ") { + matches := r.FindStringSubmatch(v) + if len(matches) == 0 { + return []string{v} + } + + if len(matches) != 2 { + panic(fmt.Errorf( + "Expected to only match 1 subexpression in %s, got %d", + v, + len(matches)-1)) + } + + // Index 1 of FindStringSubmatch contains the subexpression match + // (variable name) of the capture group. + variable := matches[1] + // toExpand contains a variable. + if _, ok := seenVars[variable]; ok { + panic(fmt.Errorf( + "Unbounded recursive interpolation of variable: %s", variable)) + } + // A map is passed-by-reference. Create a new map for + // this scope to prevent variables seen in one depth-first expansion + // to be also treated as "seen" in other depth-first traversals. + newSeenVars := map[string]bool{} + for k := range seenVars { + newSeenVars[k] = true + } + newSeenVars[variable] = true + if unexpandedVars, ok := stringListScope[variable]; ok { + for _, unexpandedVar := range unexpandedVars { + ret = append(ret, expandVarInternal(unexpandedVar, newSeenVars)...) + } + } else if unexpandedVar, ok := stringScope[variable]; ok { + ret = append(ret, expandVarInternal(unexpandedVar, newSeenVars)...) + } + } + return ret + } + + return expandVarInternal(toExpand, map[string]bool{}) +} diff --git a/cc/config/bp2build_test.go b/cc/config/bp2build_test.go new file mode 100644 index 000000000..a4745e609 --- /dev/null +++ b/cc/config/bp2build_test.go @@ -0,0 +1,117 @@ +// Copyright 2021 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 config + +import ( + "testing" +) + +func TestExpandVars(t *testing.T) { + testCases := []struct { + description string + stringScope exportedStringVariables + stringListScope exportedStringListVariables + toExpand string + expectedValues []string + }{ + { + description: "no expansion for non-interpolated value", + toExpand: "foo", + expectedValues: []string{"foo"}, + }, + { + description: "single level expansion for string var", + stringScope: exportedStringVariables{ + "foo": "bar", + }, + toExpand: "${foo}", + expectedValues: []string{"bar"}, + }, + { + description: "single level expansion string list var", + stringListScope: exportedStringListVariables{ + "foo": []string{"bar"}, + }, + toExpand: "${foo}", + expectedValues: []string{"bar"}, + }, + { + description: "mixed level expansion for string list var", + stringScope: exportedStringVariables{ + "foo": "${bar}", + "qux": "hello", + }, + stringListScope: exportedStringListVariables{ + "bar": []string{"baz", "${qux}"}, + }, + toExpand: "${foo}", + expectedValues: []string{"baz", "hello"}, + }, + { + description: "double level expansion", + stringListScope: exportedStringListVariables{ + "foo": []string{"${bar}"}, + "bar": []string{"baz"}, + }, + toExpand: "${foo}", + expectedValues: []string{"baz"}, + }, + { + description: "double level expansion with a literal", + stringListScope: exportedStringListVariables{ + "a": []string{"${b}", "c"}, + "b": []string{"d"}, + }, + toExpand: "${a}", + expectedValues: []string{"d", "c"}, + }, + { + description: "double level expansion, with two variables in a string", + stringListScope: exportedStringListVariables{ + "a": []string{"${b} ${c}"}, + "b": []string{"d"}, + "c": []string{"e"}, + }, + toExpand: "${a}", + expectedValues: []string{"d", "e"}, + }, + { + description: "triple level expansion with two variables in a string", + stringListScope: exportedStringListVariables{ + "a": []string{"${b} ${c}"}, + "b": []string{"${c}", "${d}"}, + "c": []string{"${d}"}, + "d": []string{"foo"}, + }, + toExpand: "${a}", + expectedValues: []string{"foo", "foo", "foo"}, + }, + } + + for _, testCase := range testCases { + t.Run(testCase.description, func(t *testing.T) { + output := expandVar(testCase.toExpand, testCase.stringScope, testCase.stringListScope) + if len(output) != len(testCase.expectedValues) { + t.Errorf("Expected %d values, got %d", len(testCase.expectedValues), len(output)) + } + for i, actual := range output { + expectedValue := testCase.expectedValues[i] + if actual != expectedValue { + t.Errorf("Actual value '%s' doesn't match expected value '%s'", actual, expectedValue) + } + } + }) + } +} diff --git a/cc/config/clang.go b/cc/config/clang.go index 5e46d5ac0..c484fc921 100644 --- a/cc/config/clang.go +++ b/cc/config/clang.go @@ -98,7 +98,7 @@ var ClangTidyDisableChecks = []string{ } func init() { - pctx.StaticVariable("ClangExtraCflags", strings.Join([]string{ + exportStringListStaticVariable("ClangExtraCflags", []string{ "-D__compiler_offsetof=__builtin_offsetof", // Emit address-significance table which allows linker to perform safe ICF. Clang does @@ -151,9 +151,9 @@ func init() { // This macro allows the bionic versioning.h to indirectly determine whether the // option -Wunguarded-availability is on or not. "-D__ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__", - }, " ")) + }) - pctx.StaticVariable("ClangExtraCppflags", strings.Join([]string{ + exportStringListStaticVariable("ClangExtraCppflags", []string{ // -Wimplicit-fallthrough is not enabled by -Wall. "-Wimplicit-fallthrough", @@ -162,13 +162,11 @@ func init() { // libc++'s math.h has an #include_next outside of system_headers. "-Wno-gnu-include-next", - }, " ")) + }) - pctx.StaticVariable("ClangExtraTargetCflags", strings.Join([]string{ - "-nostdlibinc", - }, " ")) + exportStringListStaticVariable("ClangExtraTargetCflags", []string{"-nostdlibinc"}) - pctx.StaticVariable("ClangExtraNoOverrideCflags", strings.Join([]string{ + exportStringListStaticVariable("ClangExtraNoOverrideCflags", []string{ "-Werror=address-of-temporary", // Bug: http://b/29823425 Disable -Wnull-dereference until the // new cases detected by this warning in Clang r271374 are @@ -203,11 +201,11 @@ func init() { "-Wno-non-c-typedef-for-linkage", // http://b/161304145 // New warnings to be fixed after clang-r407598 "-Wno-string-concatenation", // http://b/175068488 - }, " ")) + }) // Extra cflags for external third-party projects to disable warnings that // are infeasible to fix in all the external projects and their upstream repos. - pctx.StaticVariable("ClangExtraExternalCflags", strings.Join([]string{ + exportStringListStaticVariable("ClangExtraExternalCflags", []string{ "-Wno-enum-compare", "-Wno-enum-compare-switch", @@ -228,7 +226,7 @@ func init() { // http://b/165945989 "-Wno-psabi", - }, " ")) + }) } func ClangFilterUnknownCflags(cflags []string) []string { diff --git a/cc/config/global.go b/cc/config/global.go index 23106ec5f..d458311ee 100644 --- a/cc/config/global.go +++ b/cc/config/global.go @@ -165,13 +165,25 @@ func init() { commonGlobalCflags = append(commonGlobalCflags, "-fdebug-prefix-map=/proc/self/cwd=") } - pctx.StaticVariable("CommonGlobalConlyflags", strings.Join(commonGlobalConlyflags, " ")) - pctx.StaticVariable("DeviceGlobalCppflags", strings.Join(deviceGlobalCppflags, " ")) - pctx.StaticVariable("DeviceGlobalLdflags", strings.Join(deviceGlobalLdflags, " ")) - pctx.StaticVariable("DeviceGlobalLldflags", strings.Join(deviceGlobalLldflags, " ")) - pctx.StaticVariable("HostGlobalCppflags", strings.Join(hostGlobalCppflags, " ")) - pctx.StaticVariable("HostGlobalLdflags", strings.Join(hostGlobalLdflags, " ")) - pctx.StaticVariable("HostGlobalLldflags", strings.Join(hostGlobalLldflags, " ")) + exportStringListStaticVariable("CommonGlobalConlyflags", commonGlobalConlyflags) + exportStringListStaticVariable("DeviceGlobalCppflags", deviceGlobalCppflags) + exportStringListStaticVariable("DeviceGlobalLdflags", deviceGlobalLdflags) + exportStringListStaticVariable("DeviceGlobalLldflags", deviceGlobalLldflags) + exportStringListStaticVariable("HostGlobalCppflags", hostGlobalCppflags) + exportStringListStaticVariable("HostGlobalLdflags", hostGlobalLdflags) + exportStringListStaticVariable("HostGlobalLldflags", hostGlobalLldflags) + + // Export the static default CommonClangGlobalCflags to Bazel. + // TODO(187086342): handle cflags that are set in VariableFuncs. + commonClangGlobalCFlags := append( + ClangFilterUnknownCflags(commonGlobalCflags), + []string{ + "${ClangExtraCflags}", + // Default to zero initialization. + "-ftrivial-auto-var-init=zero", + "-enable-trivial-auto-var-init-zero-knowing-it-will-be-removed-from-clang", + }...) + exportedStringListVars.Set("CommonClangGlobalCflags", commonClangGlobalCFlags) pctx.VariableFunc("CommonClangGlobalCflags", func(ctx android.PackageVarContext) string { flags := ClangFilterUnknownCflags(commonGlobalCflags) @@ -190,41 +202,42 @@ func init() { // Default to zero initialization. flags = append(flags, "-ftrivial-auto-var-init=zero -enable-trivial-auto-var-init-zero-knowing-it-will-be-removed-from-clang") } - return strings.Join(flags, " ") }) + // Export the static default DeviceClangGlobalCflags to Bazel. + // TODO(187086342): handle cflags that are set in VariableFuncs. + deviceClangGlobalCflags := append(ClangFilterUnknownCflags(deviceGlobalCflags), "${ClangExtraTargetCflags}") + exportedStringListVars.Set("DeviceClangGlobalCflags", deviceClangGlobalCflags) + pctx.VariableFunc("DeviceClangGlobalCflags", func(ctx android.PackageVarContext) string { if ctx.Config().Fuchsia() { return strings.Join(ClangFilterUnknownCflags(deviceGlobalCflags), " ") } else { - return strings.Join(append(ClangFilterUnknownCflags(deviceGlobalCflags), "${ClangExtraTargetCflags}"), " ") + return strings.Join(deviceClangGlobalCflags, " ") } }) - pctx.StaticVariable("HostClangGlobalCflags", - strings.Join(ClangFilterUnknownCflags(hostGlobalCflags), " ")) - pctx.StaticVariable("NoOverrideClangGlobalCflags", - strings.Join(append(ClangFilterUnknownCflags(noOverrideGlobalCflags), "${ClangExtraNoOverrideCflags}"), " ")) - pctx.StaticVariable("CommonClangGlobalCppflags", - strings.Join(append(ClangFilterUnknownCflags(commonGlobalCppflags), "${ClangExtraCppflags}"), " ")) - - pctx.StaticVariable("ClangExternalCflags", "${ClangExtraExternalCflags}") + exportStringListStaticVariable("HostClangGlobalCflags", ClangFilterUnknownCflags(hostGlobalCflags)) + exportStringListStaticVariable("NoOverrideClangGlobalCflags", append(ClangFilterUnknownCflags(noOverrideGlobalCflags), "${ClangExtraNoOverrideCflags}")) + exportStringListStaticVariable("CommonClangGlobalCppflags", append(ClangFilterUnknownCflags(commonGlobalCppflags), "${ClangExtraCppflags}")) + exportStringListStaticVariable("ClangExternalCflags", []string{"${ClangExtraExternalCflags}"}) // Everything in these lists is a crime against abstraction and dependency tracking. // Do not add anything to this list. - pctx.PrefixedExistentPathsForSourcesVariable("CommonGlobalIncludes", "-I", - []string{ - "system/core/include", - "system/logging/liblog/include", - "system/media/audio/include", - "hardware/libhardware/include", - "hardware/libhardware_legacy/include", - "hardware/ril/include", - "frameworks/native/include", - "frameworks/native/opengl/include", - "frameworks/av/include", - }) + commonGlobalIncludes := []string{ + "system/core/include", + "system/logging/liblog/include", + "system/media/audio/include", + "hardware/libhardware/include", + "hardware/libhardware_legacy/include", + "hardware/ril/include", + "frameworks/native/include", + "frameworks/native/opengl/include", + "frameworks/av/include", + } + exportedStringListVars.Set("CommonGlobalIncludes", commonGlobalIncludes) + pctx.PrefixedExistentPathsForSourcesVariable("CommonGlobalIncludes", "-I", commonGlobalIncludes) pctx.SourcePathVariable("ClangDefaultBase", ClangDefaultBase) pctx.VariableFunc("ClangBase", func(ctx android.PackageVarContext) string { diff --git a/cc/coverage.go b/cc/coverage.go index 5b5ccf2f0..baf422637 100644 --- a/cc/coverage.go +++ b/cc/coverage.go @@ -96,7 +96,8 @@ func (cov *coverage) flags(ctx ModuleContext, flags Flags, deps PathDeps) (Flags // flags that the module may use. flags.Local.CFlags = append(flags.Local.CFlags, "-Wno-frame-larger-than=", "-O0") } else if clangCoverage { - flags.Local.CommonFlags = append(flags.Local.CommonFlags, profileInstrFlag, "-fcoverage-mapping", "-Wno-pass-failed") + flags.Local.CommonFlags = append(flags.Local.CommonFlags, profileInstrFlag, + "-fcoverage-mapping", "-Wno-pass-failed", "-D__ANDROID_CLANG_COVERAGE__") } } @@ -203,7 +204,7 @@ func SetCoverageProperties(ctx android.BaseModuleContext, properties CoveragePro type Coverage interface { android.Module IsNativeCoverageNeeded(ctx android.BaseModuleContext) bool - PreventInstall() + SetPreventInstall() HideFromMake() MarkAsCoverageVariant(bool) EnableCoverageIfNeeded() @@ -236,7 +237,7 @@ func coverageMutator(mctx android.BottomUpMutatorContext) { // to an APEX via 'data' property. m := mctx.CreateVariations("", "cov") m[0].(Coverage).MarkAsCoverageVariant(false) - m[0].(Coverage).PreventInstall() + m[0].(Coverage).SetPreventInstall() m[0].(Coverage).HideFromMake() m[1].(Coverage).MarkAsCoverageVariant(true) diff --git a/cc/image.go b/cc/image.go index 5d4171726..47a424bc2 100644 --- a/cc/image.go +++ b/cc/image.go @@ -430,6 +430,16 @@ func MutateImage(mctx android.BaseModuleContext, m ImageMutatableModule) { recoverySnapshotVersion := mctx.DeviceConfig().RecoverySnapshotVersion() usingRecoverySnapshot := recoverySnapshotVersion != "current" && recoverySnapshotVersion != "" + needVndkVersionVendorVariantForLlndk := false + if boardVndkVersion != "" { + boardVndkApiLevel, err := android.ApiLevelFromUser(mctx, boardVndkVersion) + if err == nil && !boardVndkApiLevel.IsPreview() { + // VNDK snapshot newer than v30 has LLNDK stub libraries. + // Only the VNDK version less than or equal to v30 requires generating the vendor + // variant of the VNDK version from the source tree. + needVndkVersionVendorVariantForLlndk = boardVndkApiLevel.LessThanOrEqualTo(android.ApiLevelOrPanic(mctx, "30")) + } + } if boardVndkVersion == "current" { boardVndkVersion = platformVndkVersion } @@ -446,7 +456,9 @@ func MutateImage(mctx android.BaseModuleContext, m ImageMutatableModule) { vendorVariants = append(vendorVariants, platformVndkVersion) productVariants = append(productVariants, platformVndkVersion) } - if boardVndkVersion != "" { + // Generate vendor variants for boardVndkVersion only if the VNDK snapshot does not + // provide the LLNDK stub libraries. + if needVndkVersionVendorVariantForLlndk { vendorVariants = append(vendorVariants, boardVndkVersion) } if productVndkVersion != "" { diff --git a/cc/library.go b/cc/library.go index c5ff9b1f4..c918b96ba 100644 --- a/cc/library.go +++ b/cc/library.go @@ -221,17 +221,19 @@ func RegisterLibraryBuildComponents(ctx android.RegistrationContext) { // For bp2build conversion. type bazelCcLibraryAttributes struct { // Attributes pertaining to both static and shared variants. - Srcs bazel.LabelListAttribute - Hdrs bazel.LabelListAttribute - Deps bazel.LabelListAttribute - Dynamic_deps bazel.LabelListAttribute - Whole_archive_deps bazel.LabelListAttribute - Copts bazel.StringListAttribute - Includes bazel.StringListAttribute - Linkopts bazel.StringListAttribute + Srcs bazel.LabelListAttribute + Hdrs bazel.LabelListAttribute + Deps bazel.LabelListAttribute + Implementation_deps bazel.LabelListAttribute + Dynamic_deps bazel.LabelListAttribute + Whole_archive_deps bazel.LabelListAttribute + Copts bazel.StringListAttribute + Includes bazel.StringListAttribute + Linkopts bazel.StringListAttribute // Attributes pertaining to shared variant. Shared_copts bazel.StringListAttribute Shared_srcs bazel.LabelListAttribute + Exported_deps_for_shared bazel.LabelListAttribute Static_deps_for_shared bazel.LabelListAttribute Dynamic_deps_for_shared bazel.LabelListAttribute Whole_archive_deps_for_shared bazel.LabelListAttribute @@ -240,6 +242,7 @@ type bazelCcLibraryAttributes struct { // Attributes pertaining to static variant. Static_copts bazel.StringListAttribute Static_srcs bazel.LabelListAttribute + Exported_deps_for_static bazel.LabelListAttribute Static_deps_for_static bazel.LabelListAttribute Dynamic_deps_for_static bazel.LabelListAttribute Whole_archive_deps_for_static bazel.LabelListAttribute @@ -292,7 +295,8 @@ func CcLibraryBp2Build(ctx android.TopDownMutatorContext) { attrs := &bazelCcLibraryAttributes{ Srcs: srcs, - Deps: linkerAttrs.deps, + Implementation_deps: linkerAttrs.deps, + Deps: linkerAttrs.exportedDeps, Dynamic_deps: linkerAttrs.dynamicDeps, Whole_archive_deps: linkerAttrs.wholeArchiveDeps, Copts: compilerAttrs.copts, @@ -1426,11 +1430,10 @@ func (library *libraryDecorator) linkSAbiDumpFiles(ctx ModuleContext, objs Objec } } -func processLLNDKHeaders(ctx ModuleContext, srcHeaderDir string, outDir android.ModuleGenPath) android.Path { +func processLLNDKHeaders(ctx ModuleContext, srcHeaderDir string, outDir android.ModuleGenPath) (timestamp android.Path, installPaths android.WritablePaths) { srcDir := android.PathForModuleSrc(ctx, srcHeaderDir) srcFiles := ctx.GlobFiles(filepath.Join(srcDir.String(), "**/*.h"), nil) - var installPaths []android.WritablePath for _, header := range srcFiles { headerDir := filepath.Dir(header.String()) relHeaderDir, err := filepath.Rel(srcDir.String(), headerDir) @@ -1443,7 +1446,7 @@ func processLLNDKHeaders(ctx ModuleContext, srcHeaderDir string, outDir android. installPaths = append(installPaths, outDir.Join(ctx, relHeaderDir, header.Base())) } - return processHeadersWithVersioner(ctx, srcDir, outDir, srcFiles, installPaths) + return processHeadersWithVersioner(ctx, srcDir, outDir, srcFiles, installPaths), installPaths } // link registers actions to link this library, and sets various fields @@ -1459,7 +1462,9 @@ func (library *libraryDecorator) link(ctx ModuleContext, var timestampFiles android.Paths for _, dir := range library.Properties.Llndk.Export_preprocessed_headers { - timestampFiles = append(timestampFiles, processLLNDKHeaders(ctx, dir, genHeaderOutDir)) + timestampFile, installPaths := processLLNDKHeaders(ctx, dir, genHeaderOutDir) + timestampFiles = append(timestampFiles, timestampFile) + library.addExportedGeneratedHeaders(installPaths.Paths()...) } if Bool(library.Properties.Llndk.Export_headers_as_system) { @@ -2216,14 +2221,15 @@ func maybeInjectBoringSSLHash(ctx android.ModuleContext, outputFile android.Modu } type bazelCcLibraryStaticAttributes struct { - Copts bazel.StringListAttribute - Srcs bazel.LabelListAttribute - Deps bazel.LabelListAttribute - Whole_archive_deps bazel.LabelListAttribute - Linkopts bazel.StringListAttribute - Linkstatic bool - Includes bazel.StringListAttribute - Hdrs bazel.LabelListAttribute + Copts bazel.StringListAttribute + Srcs bazel.LabelListAttribute + Implementation_deps bazel.LabelListAttribute + Deps bazel.LabelListAttribute + Whole_archive_deps bazel.LabelListAttribute + Linkopts bazel.StringListAttribute + Linkstatic bool + Includes bazel.StringListAttribute + Hdrs bazel.LabelListAttribute } type bazelCcLibraryStatic struct { @@ -2244,10 +2250,11 @@ func ccLibraryStaticBp2BuildInternal(ctx android.TopDownMutatorContext, module * exportedIncludes := bp2BuildParseExportedIncludes(ctx, module) attrs := &bazelCcLibraryStaticAttributes{ - Copts: compilerAttrs.copts, - Srcs: compilerAttrs.srcs, - Deps: linkerAttrs.deps, - Whole_archive_deps: linkerAttrs.wholeArchiveDeps, + Copts: compilerAttrs.copts, + Srcs: compilerAttrs.srcs, + Implementation_deps: linkerAttrs.deps, + Deps: linkerAttrs.exportedDeps, + Whole_archive_deps: linkerAttrs.wholeArchiveDeps, Linkopts: linkerAttrs.linkopts, Linkstatic: true, diff --git a/cc/library_headers.go b/cc/library_headers.go index 0aba8dea8..20659292d 100644 --- a/cc/library_headers.go +++ b/cc/library_headers.go @@ -109,10 +109,11 @@ func prebuiltLibraryHeaderFactory() android.Module { } type bazelCcLibraryHeadersAttributes struct { - Copts bazel.StringListAttribute - Hdrs bazel.LabelListAttribute - Includes bazel.StringListAttribute - Deps bazel.LabelListAttribute + Copts bazel.StringListAttribute + Hdrs bazel.LabelListAttribute + Includes bazel.StringListAttribute + Deps bazel.LabelListAttribute + Implementation_deps bazel.LabelListAttribute } type bazelCcLibraryHeaders struct { @@ -147,9 +148,10 @@ func CcLibraryHeadersBp2Build(ctx android.TopDownMutatorContext) { linkerAttrs := bp2BuildParseLinkerProps(ctx, module) attrs := &bazelCcLibraryHeadersAttributes{ - Copts: compilerAttrs.copts, - Includes: exportedIncludes, - Deps: linkerAttrs.deps, + Copts: compilerAttrs.copts, + Includes: exportedIncludes, + Implementation_deps: linkerAttrs.deps, + Deps: linkerAttrs.exportedDeps, } props := bazel.BazelTargetModuleProperties{ diff --git a/cc/linkable.go b/cc/linkable.go index 40a9d8b04..b583b69e7 100644 --- a/cc/linkable.go +++ b/cc/linkable.go @@ -48,6 +48,20 @@ type PlatformSanitizeable interface { // SanitizerSupported returns true if a sanitizer type is supported by this modules compiler. SanitizerSupported(t SanitizerType) bool + // MinimalRuntimeDep returns true if this module needs to link the minimal UBSan runtime, + // either because it requires it or because a dependent module which requires it to be linked in this module. + MinimalRuntimeDep() bool + + // UbsanRuntimeDep returns true if this module needs to link the full UBSan runtime, + // either because it requires it or because a dependent module which requires it to be linked in this module. + UbsanRuntimeDep() bool + + // UbsanRuntimeNeeded returns true if the full UBSan runtime is required by this module. + UbsanRuntimeNeeded() bool + + // MinimalRuntimeNeeded returns true if the minimal UBSan runtime is required by this module + MinimalRuntimeNeeded() bool + // SanitizableDepTagChecker returns a SantizableDependencyTagChecker function type. SanitizableDepTagChecker() SantizableDependencyTagChecker } @@ -60,14 +74,42 @@ type PlatformSanitizeable interface { // implementation should handle tags from both. type SantizableDependencyTagChecker func(tag blueprint.DependencyTag) bool +// Snapshottable defines those functions necessary for handling module snapshots. +type Snapshottable interface { + // SnapshotHeaders returns a list of header paths provided by this module. + SnapshotHeaders() android.Paths + + // ExcludeFromVendorSnapshot returns true if this module should be otherwise excluded from the vendor snapshot. + ExcludeFromVendorSnapshot() bool + + // ExcludeFromRecoverySnapshot returns true if this module should be otherwise excluded from the recovery snapshot. + ExcludeFromRecoverySnapshot() bool + + // SnapshotLibrary returns true if this module is a snapshot library. + IsSnapshotLibrary() bool + + // SnapshotRuntimeLibs returns a list of libraries needed by this module at runtime but which aren't build dependencies. + SnapshotRuntimeLibs() []string + + // SnapshotSharedLibs returns the list of shared library dependencies for this module. + SnapshotSharedLibs() []string + + // IsSnapshotPrebuilt returns true if this module is a snapshot prebuilt. + IsSnapshotPrebuilt() bool +} + // LinkableInterface is an interface for a type of module that is linkable in a C++ library. type LinkableInterface interface { android.Module + Snapshottable Module() android.Module CcLibrary() bool CcLibraryInterface() bool + // BaseModuleName returns the android.ModuleBase.BaseModuleName() value for this module. + BaseModuleName() string + OutputFile() android.OptionalPath CoverageFiles() android.Paths @@ -79,9 +121,6 @@ type LinkableInterface interface { BuildSharedVariant() bool SetStatic() SetShared() - Static() bool - Shared() bool - Header() bool IsPrebuilt() bool Toc() android.OptionalPath @@ -106,13 +145,29 @@ type LinkableInterface interface { // IsLlndkPublic returns true only for LLNDK (public) libs. IsLlndkPublic() bool + // HasLlndkStubs returns true if this library has a variant that will build LLNDK stubs. + HasLlndkStubs() bool + // NeedsLlndkVariants returns true if this module has LLNDK stubs or provides LLNDK headers. NeedsLlndkVariants() bool // NeedsVendorPublicLibraryVariants returns true if this module has vendor public library stubs. NeedsVendorPublicLibraryVariants() bool + //StubsVersion returns the stubs version for this module. + StubsVersion() string + + // UseVndk returns true if the module is using VNDK libraries instead of the libraries in /system/lib or /system/lib64. + // "product" and "vendor" variant modules return true for this function. + // When BOARD_VNDK_VERSION is set, vendor variants of "vendor_available: true", "vendor: true", + // "soc_specific: true" and more vendor installed modules are included here. + // When PRODUCT_PRODUCT_VNDK_VERSION is set, product variants of "vendor_available: true" or + // "product_specific: true" modules are included here. UseVndk() bool + + // IsVndkSp returns true if this is a VNDK-SP module. + IsVndkSp() bool + MustUseVendorVariant() bool IsVndk() bool IsVndkExt() bool @@ -140,6 +195,51 @@ type LinkableInterface interface { // KernelHeadersDecorator returns true if this is a kernel headers decorator module. // This is specific to cc and should always return false for all other packages. KernelHeadersDecorator() bool + + // HiddenFromMake returns true if this module is hidden from Make. + HiddenFromMake() bool + + // RelativeInstallPath returns the relative install path for this module. + RelativeInstallPath() string + + // Binary returns true if this is a binary module. + Binary() bool + + // Object returns true if this is an object module. + Object() bool + + // Rlib returns true if this is an rlib module. + Rlib() bool + + // Dylib returns true if this is an dylib module. + Dylib() bool + + // Static returns true if this is a static library module. + Static() bool + + // Shared returns true if this is a shared library module. + Shared() bool + + // Header returns true if this is a library headers module. + Header() bool + + // EverInstallable returns true if the module is ever installable + EverInstallable() bool + + // PreventInstall returns true if this module is prevented from installation. + PreventInstall() bool + + // InstallInData returns true if this module is installed in data. + InstallInData() bool + + // Installable returns a bool pointer to the module installable property. + Installable() *bool + + // Symlinks returns a list of symlinks that should be created for this module. + Symlinks() []string + + // VndkVersion returns the VNDK version string for this module. + VndkVersion() string } var ( diff --git a/cc/linker.go b/cc/linker.go index a9930ada0..5bd21eda3 100644 --- a/cc/linker.go +++ b/cc/linker.go @@ -18,7 +18,6 @@ import ( "android/soong/android" "android/soong/cc/config" "fmt" - "strconv" "github.com/google/blueprint" "github.com/google/blueprint/proptools" @@ -390,17 +389,17 @@ func (linker *baseLinker) useClangLld(ctx ModuleContext) bool { } // Check whether the SDK version is not older than the specific one -func CheckSdkVersionAtLeast(ctx ModuleContext, SdkVersion int) bool { - if ctx.sdkVersion() == "current" { +func CheckSdkVersionAtLeast(ctx ModuleContext, SdkVersion android.ApiLevel) bool { + if ctx.minSdkVersion() == "current" { return true } - parsedSdkVersion, err := strconv.Atoi(ctx.sdkVersion()) + parsedSdkVersion, err := nativeApiLevelFromUser(ctx, ctx.minSdkVersion()) if err != nil { - ctx.PropertyErrorf("sdk_version", - "Invalid sdk_version value (must be int or current): %q", - ctx.sdkVersion()) + ctx.PropertyErrorf("min_sdk_version", + "Invalid min_sdk_version value (must be int or current): %q", + ctx.minSdkVersion()) } - if parsedSdkVersion < SdkVersion { + if parsedSdkVersion.LessThan(SdkVersion) { return false } return true @@ -425,13 +424,13 @@ func (linker *baseLinker) linkerFlags(ctx ModuleContext, flags Flags) Flags { // ANDROID_RELR relocations were supported at API level >= 28. // Relocation packer was supported at API level >= 23. // Do the best we can... - if !ctx.useSdk() || CheckSdkVersionAtLeast(ctx, 30) { + if (!ctx.useSdk() && ctx.minSdkVersion() == "") || CheckSdkVersionAtLeast(ctx, android.FirstShtRelrVersion) { flags.Global.LdFlags = append(flags.Global.LdFlags, "-Wl,--pack-dyn-relocs=android+relr") - } else if CheckSdkVersionAtLeast(ctx, 28) { + } else if CheckSdkVersionAtLeast(ctx, android.FirstAndroidRelrVersion) { flags.Global.LdFlags = append(flags.Global.LdFlags, "-Wl,--pack-dyn-relocs=android+relr", "-Wl,--use-android-relr-tags") - } else if CheckSdkVersionAtLeast(ctx, 23) { + } else if CheckSdkVersionAtLeast(ctx, android.FirstPackedRelocationsVersion) { flags.Global.LdFlags = append(flags.Global.LdFlags, "-Wl,--pack-dyn-relocs=android") } } diff --git a/cc/makevars.go b/cc/makevars.go index da5f1fde2..2b326ef4f 100644 --- a/cc/makevars.go +++ b/cc/makevars.go @@ -288,9 +288,7 @@ func makeVarsToolchain(ctx android.MakeVarsContext, secondPrefix string, ctx.Strict(makePrefix+"OBJCOPY", "${config.ClangBin}/llvm-objcopy") ctx.Strict(makePrefix+"LD", "${config.ClangBin}/lld") ctx.Strict(makePrefix+"NDK_TRIPLE", config.NDKTriple(toolchain)) - // TODO: work out whether to make this "${config.ClangBin}/llvm-", which - // should mostly work, or remove it. - ctx.Strict(makePrefix+"TOOLS_PREFIX", gccCmd(toolchain, "")) + ctx.Strict(makePrefix+"TOOLS_PREFIX", "${config.ClangBin}/llvm-") // TODO: GCC version is obsolete now that GCC has been removed. ctx.Strict(makePrefix+"GCC_VERSION", toolchain.GccVersion()) } diff --git a/cc/sabi.go b/cc/sabi.go index c0eb57cc3..384dcc165 100644 --- a/cc/sabi.go +++ b/cc/sabi.go @@ -84,7 +84,7 @@ func classifySourceAbiDump(ctx android.BaseModuleContext) string { return "LLNDK" } if m.UseVndk() && m.IsVndk() && !m.IsVndkPrivate() { - if m.isVndkSp() { + if m.IsVndkSp() { if m.IsVndkExt() { return "VNDK-SP-ext" } else { diff --git a/cc/sanitize.go b/cc/sanitize.go index 397121ef5..f486ee470 100644 --- a/cc/sanitize.go +++ b/cc/sanitize.go @@ -902,7 +902,7 @@ func sanitizerDepsMutator(t SanitizerType) func(android.TopDownMutatorContext) { if d, ok := child.(PlatformSanitizeable); ok && d.SanitizePropDefined() && !d.SanitizeNever() && !d.IsSanitizerExplicitlyDisabled(t) { - if t == cfi || t == Hwasan || t == scs { + if t == cfi || t == Hwasan || t == scs || t == Asan { if d.StaticallyLinked() && d.SanitizerSupported(t) { // Rust does not support some of these sanitizers, so we need to check if it's // supported before setting this true. @@ -1075,7 +1075,7 @@ func sanitizerRuntimeMutator(mctx android.BottomUpMutatorContext) { sanitizers = append(sanitizers, "shadow-call-stack") } - if Bool(c.sanitize.Properties.Sanitize.Memtag_heap) && c.binary() { + if Bool(c.sanitize.Properties.Sanitize.Memtag_heap) && c.Binary() { noteDep := "note_memtag_heap_async" if Bool(c.sanitize.Properties.Sanitize.Diag.Memtag_heap) { noteDep = "note_memtag_heap_sync" @@ -1208,6 +1208,14 @@ type Sanitizeable interface { AddSanitizerDependencies(ctx android.BottomUpMutatorContext, sanitizerName string) } +func (c *Module) MinimalRuntimeDep() bool { + return c.sanitize.Properties.MinimalRuntimeDep +} + +func (c *Module) UbsanRuntimeDep() bool { + return c.sanitize.Properties.UbsanRuntimeDep +} + func (c *Module) SanitizePropDefined() bool { return c.sanitize != nil } @@ -1253,7 +1261,7 @@ func sanitizerMutator(t SanitizerType) func(android.BottomUpMutatorContext) { modules[0].(PlatformSanitizeable).SetSanitizer(t, true) } else if c.IsSanitizerEnabled(t) || c.SanitizeDep() { isSanitizerEnabled := c.IsSanitizerEnabled(t) - if c.StaticallyLinked() || c.Header() || t == Asan || t == Fuzzer { + if c.StaticallyLinked() || c.Header() || t == Fuzzer { // Static and header libs are split into non-sanitized and sanitized variants. // Shared libs are not split. However, for asan and fuzzer, we split even for shared // libs because a library sanitized for asan/fuzzer can't be linked from a library @@ -1441,6 +1449,14 @@ func enableMinimalRuntime(sanitize *sanitize) bool { return false } +func (m *Module) UbsanRuntimeNeeded() bool { + return enableUbsanRuntime(m.sanitize) +} + +func (m *Module) MinimalRuntimeNeeded() bool { + return enableMinimalRuntime(m.sanitize) +} + func enableUbsanRuntime(sanitize *sanitize) bool { return Bool(sanitize.Properties.Sanitize.Diag.Integer_overflow) || Bool(sanitize.Properties.Sanitize.Diag.Undefined) || diff --git a/cc/sanitize_test.go b/cc/sanitize_test.go new file mode 100644 index 000000000..f12634678 --- /dev/null +++ b/cc/sanitize_test.go @@ -0,0 +1,204 @@ +// Copyright 2021 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 ( + "testing" + + "android/soong/android" +) + +var prepareForAsanTest = android.FixtureAddFile("asan/Android.bp", []byte(` + cc_library_shared { + name: "libclang_rt.asan-aarch64-android", + } + + cc_library_shared { + name: "libclang_rt.asan-arm-android", + } +`)) + +func TestAsan(t *testing.T) { + bp := ` + cc_binary { + name: "bin_with_asan", + host_supported: true, + shared_libs: [ + "libshared", + "libasan", + ], + static_libs: [ + "libstatic", + "libnoasan", + ], + sanitize: { + address: true, + } + } + + cc_binary { + name: "bin_no_asan", + host_supported: true, + shared_libs: [ + "libshared", + "libasan", + ], + static_libs: [ + "libstatic", + "libnoasan", + ], + } + + cc_library_shared { + name: "libshared", + host_supported: true, + shared_libs: ["libtransitive"], + } + + cc_library_shared { + name: "libasan", + host_supported: true, + shared_libs: ["libtransitive"], + sanitize: { + address: true, + } + } + + cc_library_shared { + name: "libtransitive", + host_supported: true, + } + + cc_library_static { + name: "libstatic", + host_supported: true, + } + + cc_library_static { + name: "libnoasan", + host_supported: true, + sanitize: { + address: false, + } + } + ` + + result := android.GroupFixturePreparers( + prepareForCcTest, + prepareForAsanTest, + ).RunTestWithBp(t, bp) + + check := func(t *testing.T, result *android.TestResult, variant string) { + asanVariant := variant + "_asan" + sharedVariant := variant + "_shared" + sharedAsanVariant := sharedVariant + "_asan" + staticVariant := variant + "_static" + staticAsanVariant := staticVariant + "_asan" + + // The binaries, one with asan and one without + binWithAsan := result.ModuleForTests("bin_with_asan", asanVariant) + binNoAsan := result.ModuleForTests("bin_no_asan", variant) + + // Shared libraries that don't request asan + libShared := result.ModuleForTests("libshared", sharedVariant) + libTransitive := result.ModuleForTests("libtransitive", sharedVariant) + + // Shared library that requests asan + libAsan := result.ModuleForTests("libasan", sharedAsanVariant) + + // Static library that uses an asan variant for bin_with_asan and a non-asan variant + // for bin_no_asan. + libStaticAsanVariant := result.ModuleForTests("libstatic", staticAsanVariant) + libStaticNoAsanVariant := result.ModuleForTests("libstatic", staticVariant) + + // Static library that never uses asan. + libNoAsan := result.ModuleForTests("libnoasan", staticVariant) + + // expectSharedLinkDep verifies that the from module links against the to module as a + // shared library. + expectSharedLinkDep := func(from, to android.TestingModule) { + t.Helper() + fromLink := from.Description("link") + toLink := to.Description("strip") + + if g, w := fromLink.OrderOnly.Strings(), toLink.Output.String(); !android.InList(w, g) { + t.Errorf("%s should link against %s, expected %q, got %q", + from.Module(), to.Module(), w, g) + } + } + + // expectStaticLinkDep verifies that the from module links against the to module as a + // static library. + expectStaticLinkDep := func(from, to android.TestingModule) { + t.Helper() + fromLink := from.Description("link") + toLink := to.Description("static link") + + if g, w := fromLink.Implicits.Strings(), toLink.Output.String(); !android.InList(w, g) { + t.Errorf("%s should link against %s, expected %q, got %q", + from.Module(), to.Module(), w, g) + } + + } + + // expectInstallDep verifies that the install rule of the from module depends on the + // install rule of the to module. + expectInstallDep := func(from, to android.TestingModule) { + t.Helper() + fromInstalled := from.Description("install") + toInstalled := to.Description("install") + + // combine implicits and order-only dependencies, host uses implicit but device uses + // order-only. + got := append(fromInstalled.Implicits.Strings(), fromInstalled.OrderOnly.Strings()...) + want := toInstalled.Output.String() + if !android.InList(want, got) { + t.Errorf("%s installation should depend on %s, expected %q, got %q", + from.Module(), to.Module(), want, got) + } + } + + expectSharedLinkDep(binWithAsan, libShared) + expectSharedLinkDep(binWithAsan, libAsan) + expectSharedLinkDep(libShared, libTransitive) + expectSharedLinkDep(libAsan, libTransitive) + + expectStaticLinkDep(binWithAsan, libStaticAsanVariant) + expectStaticLinkDep(binWithAsan, libNoAsan) + + expectInstallDep(binWithAsan, libShared) + expectInstallDep(binWithAsan, libAsan) + expectInstallDep(binWithAsan, libTransitive) + expectInstallDep(libShared, libTransitive) + expectInstallDep(libAsan, libTransitive) + + expectSharedLinkDep(binNoAsan, libShared) + expectSharedLinkDep(binNoAsan, libAsan) + expectSharedLinkDep(libShared, libTransitive) + expectSharedLinkDep(libAsan, libTransitive) + + expectStaticLinkDep(binNoAsan, libStaticNoAsanVariant) + expectStaticLinkDep(binNoAsan, libNoAsan) + + expectInstallDep(binNoAsan, libShared) + expectInstallDep(binNoAsan, libAsan) + expectInstallDep(binNoAsan, libTransitive) + expectInstallDep(libShared, libTransitive) + expectInstallDep(libAsan, libTransitive) + } + + t.Run("host", func(t *testing.T) { check(t, result, result.Config.BuildOSTarget.String()) }) + t.Run("device", func(t *testing.T) { check(t, result, "android_arm64_armv8-a") }) +} diff --git a/cc/snapshot_prebuilt.go b/cc/snapshot_prebuilt.go index 885a0ce6d..a7351a9ef 100644 --- a/cc/snapshot_prebuilt.go +++ b/cc/snapshot_prebuilt.go @@ -35,12 +35,12 @@ type snapshotImage interface { // Function that returns true if the module is included in this image. // Using a function return instead of a value to prevent early // evalution of a function that may be not be defined. - inImage(m *Module) func() bool + inImage(m LinkableInterface) func() bool // Returns true if the module is private and must not be included in the // snapshot. For example VNDK-private modules must return true for the // vendor snapshots. But false for the recovery snapshots. - private(m *Module) bool + private(m LinkableInterface) bool // Returns true if a dir under source tree is an SoC-owned proprietary // directory, such as device/, vendor/, etc. @@ -56,7 +56,7 @@ type snapshotImage interface { // Whether a given module has been explicitly excluded from the // snapshot, e.g., using the exclude_from_vendor_snapshot or // exclude_from_recovery_snapshot properties. - excludeFromSnapshot(m *Module) bool + excludeFromSnapshot(m LinkableInterface) bool // Returns true if the build is using a snapshot for this image. isUsingSnapshot(cfg android.DeviceConfig) bool @@ -125,11 +125,11 @@ func (vendorSnapshotImage) shouldGenerateSnapshot(ctx android.SingletonContext) return ctx.DeviceConfig().VndkVersion() == "current" } -func (vendorSnapshotImage) inImage(m *Module) func() bool { +func (vendorSnapshotImage) inImage(m LinkableInterface) func() bool { return m.InVendor } -func (vendorSnapshotImage) private(m *Module) bool { +func (vendorSnapshotImage) private(m LinkableInterface) bool { return m.IsVndkPrivate() } @@ -159,7 +159,7 @@ func (vendorSnapshotImage) includeVndk() bool { return true } -func (vendorSnapshotImage) excludeFromSnapshot(m *Module) bool { +func (vendorSnapshotImage) excludeFromSnapshot(m LinkableInterface) bool { return m.ExcludeFromVendorSnapshot() } @@ -206,12 +206,12 @@ func (recoverySnapshotImage) shouldGenerateSnapshot(ctx android.SingletonContext return ctx.DeviceConfig().RecoverySnapshotVersion() == "current" } -func (recoverySnapshotImage) inImage(m *Module) func() bool { +func (recoverySnapshotImage) inImage(m LinkableInterface) func() bool { return m.InRecovery } // recovery snapshot does not have private libraries. -func (recoverySnapshotImage) private(m *Module) bool { +func (recoverySnapshotImage) private(m LinkableInterface) bool { return false } @@ -224,7 +224,7 @@ func (recoverySnapshotImage) includeVndk() bool { return false } -func (recoverySnapshotImage) excludeFromSnapshot(m *Module) bool { +func (recoverySnapshotImage) excludeFromSnapshot(m LinkableInterface) bool { return m.ExcludeFromRecoverySnapshot() } @@ -454,13 +454,30 @@ func (p *baseSnapshotDecorator) snapshotAndroidMkSuffix() string { } func (p *baseSnapshotDecorator) setSnapshotAndroidMkSuffix(ctx android.ModuleContext) { - if ctx.OtherModuleDependencyVariantExists([]blueprint.Variation{ - {Mutator: "image", Variation: android.CoreVariation}, - }, ctx.Module().(*Module).BaseModuleName()) { + coreVariations := append(ctx.Target().Variations(), blueprint.Variation{ + Mutator: "image", + Variation: android.CoreVariation}) + + if ctx.OtherModuleFarDependencyVariantExists(coreVariations, ctx.Module().(*Module).BaseModuleName()) { p.baseProperties.Androidmk_suffix = p.image.moduleNameSuffix() - } else { - p.baseProperties.Androidmk_suffix = "" + return + } + + // If there is no matching core variation, there could still be a + // product variation, for example if a module is product specific and + // vendor available. In that case, we also want to add the androidmk + // suffix. + + productVariations := append(ctx.Target().Variations(), blueprint.Variation{ + Mutator: "image", + Variation: ProductVariationPrefix + ctx.DeviceConfig().PlatformVndkVersion()}) + + if ctx.OtherModuleFarDependencyVariantExists(productVariations, ctx.Module().(*Module).BaseModuleName()) { + p.baseProperties.Androidmk_suffix = p.image.moduleNameSuffix() + return } + + p.baseProperties.Androidmk_suffix = "" } // Call this with a module suffix after creating a snapshot module, such as diff --git a/cc/snapshot_utils.go b/cc/snapshot_utils.go index c32fa364f..8eb616448 100644 --- a/cc/snapshot_utils.go +++ b/cc/snapshot_utils.go @@ -23,6 +23,36 @@ var ( headerExts = []string{".h", ".hh", ".hpp", ".hxx", ".h++", ".inl", ".inc", ".ipp", ".h.generic"} ) +func (m *Module) IsSnapshotLibrary() bool { + if _, ok := m.linker.(snapshotLibraryInterface); ok { + return true + } + return false +} + +func (m *Module) SnapshotHeaders() android.Paths { + if m.IsSnapshotLibrary() { + return m.linker.(snapshotLibraryInterface).snapshotHeaders() + } + return android.Paths{} +} + +func (m *Module) Dylib() bool { + return false +} + +func (m *Module) Rlib() bool { + return false +} + +func (m *Module) SnapshotRuntimeLibs() []string { + return m.Properties.SnapshotRuntimeLibs +} + +func (m *Module) SnapshotSharedLibs() []string { + return m.Properties.SnapshotSharedLibs +} + // snapshotLibraryInterface is an interface for libraries captured to VNDK / vendor snapshots. type snapshotLibraryInterface interface { libraryInterface @@ -68,14 +98,14 @@ func (s *snapshotMap) get(name string, arch android.ArchType) (snapshot string, return snapshot, found } -// shouldCollectHeadersForSnapshot determines if the module is a possible candidate for snapshot. +// ShouldCollectHeadersForSnapshot determines if the module is a possible candidate for snapshot. // If it's true, collectHeadersForSnapshot will be called in GenerateAndroidBuildActions. -func shouldCollectHeadersForSnapshot(ctx android.ModuleContext, m *Module, apexInfo android.ApexInfo) bool { +func ShouldCollectHeadersForSnapshot(ctx android.ModuleContext, m LinkableInterface, apexInfo android.ApexInfo) bool { if ctx.DeviceConfig().VndkVersion() != "current" && ctx.DeviceConfig().RecoverySnapshotVersion() != "current" { return false } - if _, _, ok := isVndkSnapshotAware(ctx.DeviceConfig(), m, apexInfo); ok { + if _, ok := isVndkSnapshotAware(ctx.DeviceConfig(), m, apexInfo); ok { return ctx.Config().VndkSnapshotBuildArtifacts() } diff --git a/cc/test.go b/cc/test.go index d4c23d7bf..047a69e3e 100644 --- a/cc/test.go +++ b/cc/test.go @@ -48,12 +48,19 @@ type TestOptions struct { Unit_test *bool // Add ShippingApiLevelModuleController to auto generated test config. If the device properties - // for the shipping api level is less than the test_min_api_level, skip this module. - Test_min_api_level *int64 + // for the shipping api level is less than the min_shipping_api_level, skip this module. + Min_shipping_api_level *int64 + + // Add ShippingApiLevelModuleController to auto generated test config. If any of the device + // shipping api level and vendor api level properties are less than the + // vsr_min_shipping_api_level, skip this module. + // As this includes the shipping api level check, it is not allowed to define + // min_shipping_api_level at the same time with this property. + Vsr_min_shipping_api_level *int64 // Add MinApiLevelModuleController with ro.vndk.version property. If ro.vndk.version has an - // integer value and the value is less than the test_min_vndk_version, skip this module. - Test_min_vndk_version *int64 + // integer value and the value is less than the min_vndk_version, skip this module. + Min_vndk_version *int64 } type TestBinaryProperties struct { @@ -97,7 +104,7 @@ type TestBinaryProperties struct { // Add ShippingApiLevelModuleController to auto generated test config. If the device properties // for the shipping api level is less than the test_min_api_level, skip this module. - // Deprecated (b/187258404). Use test_options.test_min_api_level instead. + // Deprecated (b/187258404). Use test_options.min_shipping_api_level instead. Test_min_api_level *int64 // Flag to indicate whether or not to create test config automatically. If AndroidTest.xml @@ -404,19 +411,30 @@ func (test *testBinary) install(ctx ModuleContext, file android.Path) { for _, tag := range test.Properties.Test_options.Test_suite_tag { configs = append(configs, tradefed.Option{Name: "test-suite-tag", Value: tag}) } - if test.Properties.Test_options.Test_min_api_level != nil { + if test.Properties.Test_options.Min_shipping_api_level != nil { + if test.Properties.Test_options.Vsr_min_shipping_api_level != nil { + ctx.PropertyErrorf("test_options.min_shipping_api_level", "must not be set at the same time as 'vsr_min_shipping_api_level'.") + } var options []tradefed.Option - options = append(options, tradefed.Option{Name: "min-api-level", Value: strconv.FormatInt(int64(*test.Properties.Test_options.Test_min_api_level), 10)}) + options = append(options, tradefed.Option{Name: "min-api-level", Value: strconv.FormatInt(int64(*test.Properties.Test_options.Min_shipping_api_level), 10)}) configs = append(configs, tradefed.Object{"module_controller", "com.android.tradefed.testtype.suite.module.ShippingApiLevelModuleController", options}) } else if test.Properties.Test_min_api_level != nil { // TODO: (b/187258404) Remove test.Properties.Test_min_api_level + if test.Properties.Test_options.Vsr_min_shipping_api_level != nil { + ctx.PropertyErrorf("test_min_api_level", "must not be set at the same time as 'vsr_min_shipping_api_level'.") + } var options []tradefed.Option options = append(options, tradefed.Option{Name: "min-api-level", Value: strconv.FormatInt(int64(*test.Properties.Test_min_api_level), 10)}) configs = append(configs, tradefed.Object{"module_controller", "com.android.tradefed.testtype.suite.module.ShippingApiLevelModuleController", options}) } - if test.Properties.Test_options.Test_min_vndk_version != nil { + if test.Properties.Test_options.Vsr_min_shipping_api_level != nil { + var options []tradefed.Option + options = append(options, tradefed.Option{Name: "vsr-min-api-level", Value: strconv.FormatInt(int64(*test.Properties.Test_options.Vsr_min_shipping_api_level), 10)}) + configs = append(configs, tradefed.Object{"module_controller", "com.android.tradefed.testtype.suite.module.ShippingApiLevelModuleController", options}) + } + if test.Properties.Test_options.Min_vndk_version != nil { var options []tradefed.Option - options = append(options, tradefed.Option{Name: "min-api-level", Value: strconv.FormatInt(int64(*test.Properties.Test_options.Test_min_vndk_version), 10)}) + options = append(options, tradefed.Option{Name: "min-api-level", Value: strconv.FormatInt(int64(*test.Properties.Test_options.Min_vndk_version), 10)}) options = append(options, tradefed.Option{Name: "api-level-prop", Value: "ro.vndk.version"}) configs = append(configs, tradefed.Object{"module_controller", "com.android.tradefed.testtype.suite.module.MinApiLevelModuleController", options}) } diff --git a/cc/vendor_snapshot.go b/cc/vendor_snapshot.go index 9ad51ad77..4e59a95f8 100644 --- a/cc/vendor_snapshot.go +++ b/cc/vendor_snapshot.go @@ -115,7 +115,7 @@ func isVendorProprietaryModule(ctx android.BaseModuleContext) bool { // still be a vendor proprietary module. This happens for cc modules // that are excluded from the vendor snapshot, and it means that the // vendor has assumed control of the framework-provided module. - if c, ok := ctx.Module().(*Module); ok { + if c, ok := ctx.Module().(LinkableInterface); ok { if c.ExcludeFromVendorSnapshot() { return true } @@ -137,7 +137,7 @@ func isRecoveryProprietaryModule(ctx android.BaseModuleContext) bool { // that are excluded from the recovery snapshot, and it means that the // vendor has assumed control of the framework-provided module. - if c, ok := ctx.Module().(*Module); ok { + if c, ok := ctx.Module().(LinkableInterface); ok { if c.ExcludeFromRecoverySnapshot() { return true } @@ -147,8 +147,8 @@ func isRecoveryProprietaryModule(ctx android.BaseModuleContext) bool { } // Determines if the module is a candidate for snapshot. -func isSnapshotAware(cfg android.DeviceConfig, m *Module, inProprietaryPath bool, apexInfo android.ApexInfo, image snapshotImage) bool { - if !m.Enabled() || m.Properties.HideFromMake { +func isSnapshotAware(cfg android.DeviceConfig, m LinkableInterface, inProprietaryPath bool, apexInfo android.ApexInfo, image snapshotImage) bool { + if !m.Enabled() || m.HiddenFromMake() { return false } // When android/prebuilt.go selects between source and prebuilt, it sets @@ -177,51 +177,51 @@ func isSnapshotAware(cfg android.DeviceConfig, m *Module, inProprietaryPath bool return false } // skip kernel_headers which always depend on vendor - if _, ok := m.linker.(*kernelHeadersDecorator); ok { + if m.KernelHeadersDecorator() { return false } - // skip LLNDK libraries which are backward compatible + if m.IsLlndk() { return false } // Libraries - if l, ok := m.linker.(snapshotLibraryInterface); ok { - if m.sanitize != nil { + if sanitizable, ok := m.(PlatformSanitizeable); ok && sanitizable.IsSnapshotLibrary() { + if sanitizable.SanitizePropDefined() { // scs and hwasan export both sanitized and unsanitized variants for static and header // Always use unsanitized variants of them. for _, t := range []SanitizerType{scs, Hwasan} { - if !l.shared() && m.sanitize.isSanitizerEnabled(t) { + if !sanitizable.Shared() && sanitizable.IsSanitizerEnabled(t) { return false } } // cfi also exports both variants. But for static, we capture both. // This is because cfi static libraries can't be linked from non-cfi modules, // and vice versa. This isn't the case for scs and hwasan sanitizers. - if !l.static() && !l.shared() && m.sanitize.isSanitizerEnabled(cfi) { + if !sanitizable.Static() && !sanitizable.Shared() && sanitizable.IsSanitizerEnabled(cfi) { return false } } - if l.static() { - return m.outputFile.Valid() && !image.private(m) + if sanitizable.Static() { + return sanitizable.OutputFile().Valid() && !image.private(m) } - if l.shared() { - if !m.outputFile.Valid() { + if sanitizable.Shared() { + if !sanitizable.OutputFile().Valid() { return false } if image.includeVndk() { - if !m.IsVndk() { + if !sanitizable.IsVndk() { return true } - return m.IsVndkExt() + return sanitizable.IsVndkExt() } } return true } // Binaries and Objects - if m.binary() || m.object() { - return m.outputFile.Valid() + if m.Binary() || m.Object() { + return m.OutputFile().Valid() } return false @@ -323,7 +323,7 @@ func (c *snapshotSingleton) GenerateBuildActions(ctx android.SingletonContext) { // installSnapshot function copies prebuilt file (.so, .a, or executable) and json flag file. // For executables, init_rc and vintf_fragments files are also copied. - installSnapshot := func(m *Module, fake bool) android.Paths { + installSnapshot := func(m LinkableInterface, fake bool) android.Paths { targetArch := "arch-" + m.Target().Arch.ArchType.String() if m.Target().Arch.ArchVariant != "" { targetArch += "-" + m.Target().Arch.ArchVariant @@ -337,7 +337,7 @@ func (c *snapshotSingleton) GenerateBuildActions(ctx android.SingletonContext) { prop.ModuleName = ctx.ModuleName(m) if c.supportsVndkExt && m.IsVndkExt() { // vndk exts are installed to /vendor/lib(64)?/vndk(-sp)? - if m.isVndkSp() { + if m.IsVndkSp() { prop.RelativeInstallPath = "vndk-sp" } else { prop.RelativeInstallPath = "vndk" @@ -345,7 +345,7 @@ func (c *snapshotSingleton) GenerateBuildActions(ctx android.SingletonContext) { } else { prop.RelativeInstallPath = m.RelativeInstallPath() } - prop.RuntimeLibs = m.Properties.SnapshotRuntimeLibs + prop.RuntimeLibs = m.SnapshotRuntimeLibs() prop.Required = m.RequiredModuleNames() for _, path := range m.InitRc() { prop.InitRc = append(prop.InitRc, filepath.Join("configs", path.Base())) @@ -365,8 +365,8 @@ func (c *snapshotSingleton) GenerateBuildActions(ctx android.SingletonContext) { var propOut string - if l, ok := m.linker.(snapshotLibraryInterface); ok { - exporterInfo := ctx.ModuleProvider(m, FlagExporterInfoProvider).(FlagExporterInfo) + if m.IsSnapshotLibrary() { + exporterInfo := ctx.ModuleProvider(m.Module(), FlagExporterInfoProvider).(FlagExporterInfo) // library flags prop.ExportedFlags = exporterInfo.Flags @@ -376,19 +376,22 @@ func (c *snapshotSingleton) GenerateBuildActions(ctx android.SingletonContext) { for _, dir := range exporterInfo.SystemIncludeDirs { prop.ExportedSystemDirs = append(prop.ExportedSystemDirs, filepath.Join("include", dir.String())) } + // shared libs dependencies aren't meaningful on static or header libs - if l.shared() { - prop.SharedLibs = m.Properties.SnapshotSharedLibs + if m.Shared() { + prop.SharedLibs = m.SnapshotSharedLibs() } - if l.static() && m.sanitize != nil { - prop.SanitizeMinimalDep = m.sanitize.Properties.MinimalRuntimeDep || enableMinimalRuntime(m.sanitize) - prop.SanitizeUbsanDep = m.sanitize.Properties.UbsanRuntimeDep || enableUbsanRuntime(m.sanitize) + if sanitizable, ok := m.(PlatformSanitizeable); ok { + if sanitizable.Static() && sanitizable.SanitizePropDefined() { + prop.SanitizeMinimalDep = sanitizable.MinimalRuntimeDep() || sanitizable.MinimalRuntimeNeeded() + prop.SanitizeUbsanDep = sanitizable.UbsanRuntimeDep() || sanitizable.UbsanRuntimeNeeded() + } } var libType string - if l.static() { + if m.Static() { libType = "static" - } else if l.shared() { + } else if m.Shared() { libType = "shared" } else { libType = "header" @@ -398,16 +401,18 @@ func (c *snapshotSingleton) GenerateBuildActions(ctx android.SingletonContext) { // install .a or .so if libType != "header" { - libPath := m.outputFile.Path() + libPath := m.OutputFile().Path() stem = libPath.Base() - if l.static() && m.sanitize != nil && m.sanitize.isSanitizerEnabled(cfi) { - // both cfi and non-cfi variant for static libraries can exist. - // attach .cfi to distinguish between cfi and non-cfi. - // e.g. libbase.a -> libbase.cfi.a - ext := filepath.Ext(stem) - stem = strings.TrimSuffix(stem, ext) + ".cfi" + ext - prop.Sanitize = "cfi" - prop.ModuleName += ".cfi" + if sanitizable, ok := m.(PlatformSanitizeable); ok { + if sanitizable.Static() && sanitizable.SanitizePropDefined() && sanitizable.IsSanitizerEnabled(cfi) { + // both cfi and non-cfi variant for static libraries can exist. + // attach .cfi to distinguish between cfi and non-cfi. + // e.g. libbase.a -> libbase.cfi.a + ext := filepath.Ext(stem) + stem = strings.TrimSuffix(stem, ext) + ".cfi" + ext + prop.Sanitize = "cfi" + prop.ModuleName += ".cfi" + } } snapshotLibOut := filepath.Join(snapshotArchDir, targetArch, libType, stem) ret = append(ret, copyFile(ctx, libPath, snapshotLibOut, fake)) @@ -416,20 +421,20 @@ func (c *snapshotSingleton) GenerateBuildActions(ctx android.SingletonContext) { } propOut = filepath.Join(snapshotArchDir, targetArch, libType, stem+".json") - } else if m.binary() { + } else if m.Binary() { // binary flags prop.Symlinks = m.Symlinks() - prop.SharedLibs = m.Properties.SnapshotSharedLibs + prop.SharedLibs = m.SnapshotSharedLibs() // install bin - binPath := m.outputFile.Path() + binPath := m.OutputFile().Path() snapshotBinOut := filepath.Join(snapshotArchDir, targetArch, "binary", binPath.Base()) ret = append(ret, copyFile(ctx, binPath, snapshotBinOut, fake)) propOut = snapshotBinOut + ".json" - } else if m.object() { + } else if m.Object() { // object files aren't installed to the device, so their names can conflict. // Use module name as stem. - objPath := m.outputFile.Path() + objPath := m.OutputFile().Path() snapshotObjOut := filepath.Join(snapshotArchDir, targetArch, "object", ctx.ModuleName(m)+filepath.Ext(objPath.Base())) ret = append(ret, copyFile(ctx, objPath, snapshotObjOut, fake)) @@ -450,7 +455,7 @@ func (c *snapshotSingleton) GenerateBuildActions(ctx android.SingletonContext) { } ctx.VisitAllModules(func(module android.Module) { - m, ok := module.(*Module) + m, ok := module.(LinkableInterface) if !ok { return } @@ -484,8 +489,8 @@ func (c *snapshotSingleton) GenerateBuildActions(ctx android.SingletonContext) { // installSnapshot installs prebuilts and json flag files snapshotOutputs = append(snapshotOutputs, installSnapshot(m, installAsFake)...) // just gather headers and notice files here, because they are to be deduplicated - if l, ok := m.linker.(snapshotLibraryInterface); ok { - headers = append(headers, l.snapshotHeaders()...) + if m.IsSnapshotLibrary() { + headers = append(headers, m.SnapshotHeaders()...) } if len(m.NoticeFiles()) > 0 { diff --git a/cc/vendor_snapshot_test.go b/cc/vendor_snapshot_test.go index fddd72aa1..c3b5e8c20 100644 --- a/cc/vendor_snapshot_test.go +++ b/cc/vendor_snapshot_test.go @@ -309,6 +309,13 @@ func TestVendorSnapshotUse(t *testing.T) { compile_multilib: "64", } + cc_library { + name: "libllndk", + llndk: { + symbol_file: "libllndk.map.txt", + }, + } + cc_binary { name: "bin", vendor: true, @@ -332,7 +339,7 @@ func TestVendorSnapshotUse(t *testing.T) { vndkBp := ` vndk_prebuilt_shared { name: "libvndk", - version: "30", + version: "31", target_arch: "arm64", vendor_available: true, product_available: true, @@ -376,7 +383,7 @@ func TestVendorSnapshotUse(t *testing.T) { // different arch snapshot which has to be ignored vndk_prebuilt_shared { name: "libvndk", - version: "30", + version: "31", target_arch: "arm", vendor_available: true, product_available: true, @@ -390,6 +397,22 @@ func TestVendorSnapshotUse(t *testing.T) { }, }, } + + vndk_prebuilt_shared { + name: "libllndk", + version: "31", + target_arch: "arm64", + vendor_available: true, + product_available: true, + arch: { + arm64: { + srcs: ["libllndk.so"], + }, + arm: { + srcs: ["libllndk.so"], + }, + }, + } ` vendorProprietaryBp := ` @@ -409,7 +432,7 @@ func TestVendorSnapshotUse(t *testing.T) { no_libcrt: true, stl: "none", system_shared_libs: [], - shared_libs: ["libvndk", "libvendor_available"], + shared_libs: ["libvndk", "libvendor_available", "libllndk"], static_libs: ["libvendor", "libvendor_without_snapshot"], arch: { arm64: { @@ -449,16 +472,17 @@ func TestVendorSnapshotUse(t *testing.T) { vendor_snapshot { name: "vendor_snapshot", - version: "30", + version: "31", arch: { arm64: { vndk_libs: [ "libvndk", + "libllndk", ], static_libs: [ "libc++_static", "libc++demangle", - "libgcc_stripped", + "libunwind", "libvendor", "libvendor_available", "libvndk", @@ -476,6 +500,7 @@ func TestVendorSnapshotUse(t *testing.T) { arm: { vndk_libs: [ "libvndk", + "libllndk", ], static_libs: [ "libvendor", @@ -497,7 +522,7 @@ func TestVendorSnapshotUse(t *testing.T) { vendor_snapshot_static { name: "libvndk", - version: "30", + version: "31", target_arch: "arm64", compile_multilib: "both", vendor: true, @@ -515,7 +540,7 @@ func TestVendorSnapshotUse(t *testing.T) { vendor_snapshot_shared { name: "libvendor", - version: "30", + version: "31", target_arch: "arm64", compile_multilib: "both", vendor: true, @@ -538,7 +563,7 @@ func TestVendorSnapshotUse(t *testing.T) { vendor_snapshot_static { name: "lib32", - version: "30", + version: "31", target_arch: "arm64", compile_multilib: "32", vendor: true, @@ -551,7 +576,7 @@ func TestVendorSnapshotUse(t *testing.T) { vendor_snapshot_shared { name: "lib32", - version: "30", + version: "31", target_arch: "arm64", compile_multilib: "32", vendor: true, @@ -564,7 +589,7 @@ func TestVendorSnapshotUse(t *testing.T) { vendor_snapshot_static { name: "lib64", - version: "30", + version: "31", target_arch: "arm64", compile_multilib: "64", vendor: true, @@ -577,7 +602,7 @@ func TestVendorSnapshotUse(t *testing.T) { vendor_snapshot_shared { name: "lib64", - version: "30", + version: "31", target_arch: "arm64", compile_multilib: "64", vendor: true, @@ -590,7 +615,7 @@ func TestVendorSnapshotUse(t *testing.T) { vendor_snapshot_static { name: "libvendor", - version: "30", + version: "31", target_arch: "arm64", compile_multilib: "both", vendor: true, @@ -616,7 +641,7 @@ func TestVendorSnapshotUse(t *testing.T) { vendor_snapshot_shared { name: "libvendor_available", - version: "30", + version: "31", target_arch: "arm64", compile_multilib: "both", vendor: true, @@ -634,7 +659,7 @@ func TestVendorSnapshotUse(t *testing.T) { vendor_snapshot_static { name: "libvendor_available", - version: "30", + version: "31", target_arch: "arm64", compile_multilib: "both", vendor: true, @@ -652,7 +677,7 @@ func TestVendorSnapshotUse(t *testing.T) { vendor_snapshot_static { name: "libc++_static", - version: "30", + version: "31", target_arch: "arm64", compile_multilib: "64", vendor: true, @@ -665,7 +690,7 @@ func TestVendorSnapshotUse(t *testing.T) { vendor_snapshot_static { name: "libc++demangle", - version: "30", + version: "31", target_arch: "arm64", compile_multilib: "64", vendor: true, @@ -677,21 +702,21 @@ func TestVendorSnapshotUse(t *testing.T) { } vendor_snapshot_static { - name: "libgcc_stripped", - version: "30", + name: "libunwind", + version: "31", target_arch: "arm64", compile_multilib: "64", vendor: true, arch: { arm64: { - src: "libgcc_stripped.a", + src: "libunwind.a", }, }, } vendor_snapshot_binary { name: "bin", - version: "30", + version: "31", target_arch: "arm64", compile_multilib: "64", vendor: true, @@ -704,7 +729,7 @@ func TestVendorSnapshotUse(t *testing.T) { vendor_snapshot_binary { name: "bin32", - version: "30", + version: "31", target_arch: "arm64", compile_multilib: "32", vendor: true, @@ -732,7 +757,7 @@ func TestVendorSnapshotUse(t *testing.T) { // different arch snapshot which has to be ignored vendor_snapshot_binary { name: "bin", - version: "30", + version: "31", target_arch: "arm", compile_multilib: "first", vendor: true, @@ -759,7 +784,7 @@ func TestVendorSnapshotUse(t *testing.T) { "vendor/include/libvendor_cfi/c.h": nil, "vendor/libc++_static.a": nil, "vendor/libc++demangle.a": nil, - "vendor/libgcc_striped.a": nil, + "vendor/libunwind.a": nil, "vendor/libvndk.a": nil, "vendor/libvendor.a": nil, "vendor/libvendor.cfi.a": nil, @@ -771,11 +796,12 @@ func TestVendorSnapshotUse(t *testing.T) { "vndk/Android.bp": []byte(vndkBp), "vndk/include/libvndk/a.h": nil, "vndk/libvndk.so": nil, + "vndk/libllndk.so": nil, } config := TestConfig(t.TempDir(), android.Android, nil, "", mockFS) - config.TestProductVariables.DeviceVndkVersion = StringPtr("30") - config.TestProductVariables.Platform_vndk_version = StringPtr("31") + config.TestProductVariables.DeviceVndkVersion = StringPtr("31") + config.TestProductVariables.Platform_vndk_version = StringPtr("32") ctx := CreateTestContext(config) ctx.Register() @@ -784,17 +810,17 @@ func TestVendorSnapshotUse(t *testing.T) { _, errs = ctx.PrepareBuildActions(config) android.FailIfErrored(t, errs) - sharedVariant := "android_vendor.30_arm64_armv8-a_shared" - staticVariant := "android_vendor.30_arm64_armv8-a_static" - binaryVariant := "android_vendor.30_arm64_armv8-a" + sharedVariant := "android_vendor.31_arm64_armv8-a_shared" + staticVariant := "android_vendor.31_arm64_armv8-a_static" + binaryVariant := "android_vendor.31_arm64_armv8-a" - sharedCfiVariant := "android_vendor.30_arm64_armv8-a_shared_cfi" - staticCfiVariant := "android_vendor.30_arm64_armv8-a_static_cfi" + sharedCfiVariant := "android_vendor.31_arm64_armv8-a_shared_cfi" + staticCfiVariant := "android_vendor.31_arm64_armv8-a_static_cfi" - shared32Variant := "android_vendor.30_arm_armv7-a-neon_shared" - binary32Variant := "android_vendor.30_arm_armv7-a-neon" + shared32Variant := "android_vendor.31_arm_armv7-a-neon_shared" + binary32Variant := "android_vendor.31_arm_armv7-a-neon" - // libclient uses libvndk.vndk.30.arm64, libvendor.vendor_static.30.arm64, libvendor_without_snapshot + // libclient uses libvndk.vndk.31.arm64, libvendor.vendor_static.31.arm64, libvendor_without_snapshot libclientCcFlags := ctx.ModuleForTests("libclient", sharedVariant).Rule("cc").Args["cFlags"] for _, includeFlags := range []string{ "-Ivndk/include/libvndk", // libvndk @@ -808,8 +834,9 @@ func TestVendorSnapshotUse(t *testing.T) { libclientLdFlags := ctx.ModuleForTests("libclient", sharedVariant).Rule("ld").Args["libFlags"] for _, input := range [][]string{ - []string{sharedVariant, "libvndk.vndk.30.arm64"}, - []string{staticVariant, "libvendor.vendor_static.30.arm64"}, + []string{sharedVariant, "libvndk.vndk.31.arm64"}, + []string{sharedVariant, "libllndk.vndk.31.arm64"}, + []string{staticVariant, "libvendor.vendor_static.31.arm64"}, []string{staticVariant, "libvendor_without_snapshot"}, } { outputPaths := getOutputPaths(ctx, input[0] /* variant */, []string{input[1]} /* module name */) @@ -819,7 +846,7 @@ func TestVendorSnapshotUse(t *testing.T) { } libclientAndroidMkSharedLibs := ctx.ModuleForTests("libclient", sharedVariant).Module().(*Module).Properties.AndroidMkSharedLibs - if g, w := libclientAndroidMkSharedLibs, []string{"libvndk.vendor", "libvendor_available.vendor", "lib64"}; !reflect.DeepEqual(g, w) { + if g, w := libclientAndroidMkSharedLibs, []string{"libvndk.vendor", "libvendor_available.vendor", "libllndk.vendor", "lib64"}; !reflect.DeepEqual(g, w) { t.Errorf("wanted libclient AndroidMkSharedLibs %q, got %q", w, g) } @@ -829,11 +856,11 @@ func TestVendorSnapshotUse(t *testing.T) { } libclient32AndroidMkSharedLibs := ctx.ModuleForTests("libclient", shared32Variant).Module().(*Module).Properties.AndroidMkSharedLibs - if g, w := libclient32AndroidMkSharedLibs, []string{"libvndk.vendor", "libvendor_available.vendor", "lib32"}; !reflect.DeepEqual(g, w) { + if g, w := libclient32AndroidMkSharedLibs, []string{"libvndk.vendor", "libvendor_available.vendor", "libllndk.vendor", "lib32"}; !reflect.DeepEqual(g, w) { t.Errorf("wanted libclient32 AndroidMkSharedLibs %q, got %q", w, g) } - // libclient_cfi uses libvendor.vendor_static.30.arm64's cfi variant + // libclient_cfi uses libvendor.vendor_static.31.arm64's cfi variant libclientCfiCcFlags := ctx.ModuleForTests("libclient_cfi", sharedCfiVariant).Rule("cc").Args["cFlags"] if !strings.Contains(libclientCfiCcFlags, "-Ivendor/include/libvendor_cfi") { t.Errorf("flags for libclient_cfi must contain %#v, but was %#v.", @@ -841,12 +868,12 @@ func TestVendorSnapshotUse(t *testing.T) { } libclientCfiLdFlags := ctx.ModuleForTests("libclient_cfi", sharedCfiVariant).Rule("ld").Args["libFlags"] - libvendorCfiOutputPaths := getOutputPaths(ctx, staticCfiVariant, []string{"libvendor.vendor_static.30.arm64"}) + libvendorCfiOutputPaths := getOutputPaths(ctx, staticCfiVariant, []string{"libvendor.vendor_static.31.arm64"}) if !strings.Contains(libclientCfiLdFlags, libvendorCfiOutputPaths[0].String()) { t.Errorf("libflags for libclientCfi must contain %#v, but was %#v", libvendorCfiOutputPaths[0], libclientCfiLdFlags) } - // bin_without_snapshot uses libvndk.vendor_static.30.arm64 (which reexports vndk's exported headers) + // bin_without_snapshot uses libvndk.vendor_static.31.arm64 (which reexports vndk's exported headers) binWithoutSnapshotCcFlags := ctx.ModuleForTests("bin_without_snapshot", binaryVariant).Rule("cc").Args["cFlags"] if !strings.Contains(binWithoutSnapshotCcFlags, "-Ivndk/include/libvndk") { t.Errorf("flags for bin_without_snapshot must contain %#v, but was %#v.", @@ -854,37 +881,37 @@ func TestVendorSnapshotUse(t *testing.T) { } binWithoutSnapshotLdFlags := ctx.ModuleForTests("bin_without_snapshot", binaryVariant).Rule("ld").Args["libFlags"] - libVndkStaticOutputPaths := getOutputPaths(ctx, staticVariant, []string{"libvndk.vendor_static.30.arm64"}) + libVndkStaticOutputPaths := getOutputPaths(ctx, staticVariant, []string{"libvndk.vendor_static.31.arm64"}) if !strings.Contains(binWithoutSnapshotLdFlags, libVndkStaticOutputPaths[0].String()) { t.Errorf("libflags for bin_without_snapshot must contain %#v, but was %#v", libVndkStaticOutputPaths[0], binWithoutSnapshotLdFlags) } - // libvendor.so is installed by libvendor.vendor_shared.30.arm64 - ctx.ModuleForTests("libvendor.vendor_shared.30.arm64", sharedVariant).Output("libvendor.so") + // libvendor.so is installed by libvendor.vendor_shared.31.arm64 + ctx.ModuleForTests("libvendor.vendor_shared.31.arm64", sharedVariant).Output("libvendor.so") - // lib64.so is installed by lib64.vendor_shared.30.arm64 - ctx.ModuleForTests("lib64.vendor_shared.30.arm64", sharedVariant).Output("lib64.so") + // lib64.so is installed by lib64.vendor_shared.31.arm64 + ctx.ModuleForTests("lib64.vendor_shared.31.arm64", sharedVariant).Output("lib64.so") - // lib32.so is installed by lib32.vendor_shared.30.arm64 - ctx.ModuleForTests("lib32.vendor_shared.30.arm64", shared32Variant).Output("lib32.so") + // lib32.so is installed by lib32.vendor_shared.31.arm64 + ctx.ModuleForTests("lib32.vendor_shared.31.arm64", shared32Variant).Output("lib32.so") - // libvendor_available.so is installed by libvendor_available.vendor_shared.30.arm64 - ctx.ModuleForTests("libvendor_available.vendor_shared.30.arm64", sharedVariant).Output("libvendor_available.so") + // libvendor_available.so is installed by libvendor_available.vendor_shared.31.arm64 + ctx.ModuleForTests("libvendor_available.vendor_shared.31.arm64", sharedVariant).Output("libvendor_available.so") // libvendor_without_snapshot.so is installed by libvendor_without_snapshot ctx.ModuleForTests("libvendor_without_snapshot", sharedVariant).Output("libvendor_without_snapshot.so") - // bin is installed by bin.vendor_binary.30.arm64 - ctx.ModuleForTests("bin.vendor_binary.30.arm64", binaryVariant).Output("bin") + // bin is installed by bin.vendor_binary.31.arm64 + ctx.ModuleForTests("bin.vendor_binary.31.arm64", binaryVariant).Output("bin") - // bin32 is installed by bin32.vendor_binary.30.arm64 - ctx.ModuleForTests("bin32.vendor_binary.30.arm64", binary32Variant).Output("bin32") + // bin32 is installed by bin32.vendor_binary.31.arm64 + ctx.ModuleForTests("bin32.vendor_binary.31.arm64", binary32Variant).Output("bin32") // bin_without_snapshot is installed by bin_without_snapshot ctx.ModuleForTests("bin_without_snapshot", binaryVariant).Output("bin_without_snapshot") - // libvendor, libvendor_available and bin don't have vendor.30 variant + // libvendor, libvendor_available and bin don't have vendor.31 variant libvendorVariants := ctx.ModuleVariantsForTests("libvendor") if inList(sharedVariant, libvendorVariants) { t.Errorf("libvendor must not have variant %#v, but it does", sharedVariant) diff --git a/cc/vndk.go b/cc/vndk.go index 8b3788bbb..0254edc66 100644 --- a/cc/vndk.go +++ b/cc/vndk.go @@ -372,7 +372,7 @@ func IsForVndkApex(mctx android.BottomUpMutatorContext, m *Module) bool { if mctx.ModuleName() == "libz" { return false } - return m.ImageVariation().Variation == android.CoreVariation && lib.shared() && m.isVndkSp() + return m.ImageVariation().Variation == android.CoreVariation && lib.shared() && m.IsVndkSp() } useCoreVariant := m.VndkVersion() == mctx.DeviceConfig().PlatformVndkVersion() && @@ -574,38 +574,37 @@ type vndkSnapshotSingleton struct { vndkSnapshotZipFile android.OptionalPath } -func isVndkSnapshotAware(config android.DeviceConfig, m *Module, - apexInfo android.ApexInfo) (i snapshotLibraryInterface, vndkType string, isVndkSnapshotLib bool) { +func isVndkSnapshotAware(config android.DeviceConfig, m LinkableInterface, + apexInfo android.ApexInfo) (vndkType string, isVndkSnapshotLib bool) { if m.Target().NativeBridge == android.NativeBridgeEnabled { - return nil, "", false + return "", false } // !inVendor: There's product/vendor variants for VNDK libs. We only care about vendor variants. // !installable: Snapshot only cares about "installable" modules. // !m.IsLlndk: llndk stubs are required for building against snapshots. // IsSnapshotPrebuilt: Snapshotting a snapshot doesn't make sense. // !outputFile.Valid: Snapshot requires valid output file. - if !m.InVendor() || (!m.installable(apexInfo) && !m.IsLlndk()) || m.IsSnapshotPrebuilt() || !m.outputFile.Valid() { - return nil, "", false + if !m.InVendor() || (!installable(m, apexInfo) && !m.IsLlndk()) || m.IsSnapshotPrebuilt() || !m.OutputFile().Valid() { + return "", false } - l, ok := m.linker.(snapshotLibraryInterface) - if !ok || !l.shared() { - return nil, "", false + if !m.IsSnapshotLibrary() || !m.Shared() { + return "", false } if m.VndkVersion() == config.PlatformVndkVersion() { if m.IsVndk() && !m.IsVndkExt() { - if m.isVndkSp() { - return l, "vndk-sp", true + if m.IsVndkSp() { + return "vndk-sp", true } else { - return l, "vndk-core", true + return "vndk-core", true } - } else if l.hasLLNDKStubs() && l.stubsVersion() == "" { + } else if m.HasLlndkStubs() && m.StubsVersion() == "" { // Use default version for the snapshot. - return l, "llndk-stub", true + return "llndk-stub", true } } - return nil, "", false + return "", false } func (c *vndkSnapshotSingleton) GenerateBuildActions(ctx android.SingletonContext) { @@ -722,7 +721,7 @@ func (c *vndkSnapshotSingleton) GenerateBuildActions(ctx android.SingletonContex apexInfo := ctx.ModuleProvider(module, android.ApexInfoProvider).(android.ApexInfo) - l, vndkType, ok := isVndkSnapshotAware(ctx.DeviceConfig(), m, apexInfo) + vndkType, ok := isVndkSnapshotAware(ctx.DeviceConfig(), m, apexInfo) if !ok { return } @@ -761,7 +760,7 @@ func (c *vndkSnapshotSingleton) GenerateBuildActions(ctx android.SingletonContex } if ctx.Config().VndkSnapshotBuildArtifacts() { - headers = append(headers, l.snapshotHeaders()...) + headers = append(headers, m.SnapshotHeaders()...) } }) diff --git a/cmd/soong_build/main.go b/cmd/soong_build/main.go index 044689e67..70c88563f 100644 --- a/cmd/soong_build/main.go +++ b/cmd/soong_build/main.go @@ -17,6 +17,7 @@ package main import ( "flag" "fmt" + "io/fs" "io/ioutil" "os" "path/filepath" @@ -357,6 +358,80 @@ func touch(path string) { } } +// Find BUILD files in the srcDir which... +// +// - are not on the allow list (android/bazel.go#ShouldKeepExistingBuildFileForDir()) +// +// - won't be overwritten by corresponding bp2build generated files +// +// And return their paths so they can be left out of the Bazel workspace dir (i.e. ignored) +func getPathsToIgnoredBuildFiles(topDir string, outDir string, generatedRoot string) ([]string, error) { + paths := make([]string, 0) + + err := filepath.WalkDir(topDir, func(fFullPath string, fDirEntry fs.DirEntry, err error) error { + if err != nil { + // Warn about error, but continue trying to walk the directory tree + fmt.Fprintf(os.Stderr, "WARNING: Error accessing path '%s', err: %s\n", fFullPath, err) + return nil + } + if fDirEntry.IsDir() { + // Don't ignore entire directories + return nil + } + if !(fDirEntry.Name() == "BUILD" || fDirEntry.Name() == "BUILD.bazel") { + // Don't ignore this file - it is not a build file + return nil + } + f := strings.TrimPrefix(fFullPath, topDir+"/") + if strings.HasPrefix(f, ".repo/") { + // Don't check for files to ignore in the .repo dir (recursively) + return fs.SkipDir + } + if strings.HasPrefix(f, outDir+"/") { + // Don't check for files to ignore in the out dir (recursively) + return fs.SkipDir + } + if strings.HasPrefix(f, generatedRoot) { + // Don't check for files to ignore in the bp2build dir (recursively) + // NOTE: This is usually under outDir + return fs.SkipDir + } + fDir := filepath.Dir(f) + if android.ShouldKeepExistingBuildFileForDir(fDir) { + // Don't ignore this existing build file + return nil + } + f_bp2build := shared.JoinPath(topDir, generatedRoot, f) + if _, err := os.Stat(f_bp2build); err == nil { + // If bp2build generated an alternate BUILD file, don't exclude this workspace path + // BUILD file clash resolution happens later in the symlink forest creation + return nil + } + fmt.Fprintf(os.Stderr, "Ignoring existing BUILD file: %s\n", f) + paths = append(paths, f) + return nil + }) + + return paths, err +} + +// Returns temporary symlink forest excludes necessary for bazel build //external/... (and bazel build //frameworks/...) to work +func getTemporaryExcludes() []string { + excludes := make([]string, 0) + + // FIXME: 'autotest_lib' is a symlink back to external/autotest, and this causes an infinite symlink expansion error for Bazel + excludes = append(excludes, "external/autotest/venv/autotest_lib") + + // FIXME: The external/google-fruit/extras/bazel_root/third_party/fruit dir is poison + // It contains several symlinks back to real source dirs, and those source dirs contain BUILD files we want to ignore + excludes = append(excludes, "external/google-fruit/extras/bazel_root/third_party/fruit") + + // FIXME: 'frameworks/compile/slang' has a filegroup error due to an escaping issue + excludes = append(excludes, "frameworks/compile/slang") + + return excludes +} + // Run Soong in the bp2build mode. This creates a standalone context that registers // an alternate pipeline of mutators and singletons specifically for generating // Bazel BUILD files instead of Ninja files. @@ -415,6 +490,18 @@ func runBp2Build(configuration android.Config, extraNinjaDeps []string) { excludes = append(excludes, bootstrap.CmdlineArgs.NinjaBuildDir) } + // FIXME: Don't hardcode this here + topLevelOutDir := "out" + + pathsToIgnoredBuildFiles, err := getPathsToIgnoredBuildFiles(topDir, topLevelOutDir, generatedRoot) + if err != nil { + fmt.Fprintf(os.Stderr, "Error walking SrcDir: '%s': %s\n", configuration.SrcDir(), err) + os.Exit(1) + } + excludes = append(excludes, pathsToIgnoredBuildFiles...) + + excludes = append(excludes, getTemporaryExcludes()...) + symlinkForestDeps := bp2build.PlantSymlinkForest( topDir, workspaceRoot, generatedRoot, configuration.SrcDir(), excludes) diff --git a/dexpreopt/config.go b/dexpreopt/config.go index 1844bce6e..0bcec17b5 100644 --- a/dexpreopt/config.go +++ b/dexpreopt/config.go @@ -130,9 +130,11 @@ type ModuleConfig struct { ProvidesUsesLibrary string // library name (usually the same as module name) ClassLoaderContexts ClassLoaderContextMap - Archs []android.ArchType - DexPreoptImagesDeps []android.OutputPaths - DexPreoptImageLocationsOnHost []string // boot image location on host (file path without the arch subdirectory) + Archs []android.ArchType + DexPreoptImagesDeps []android.OutputPaths + + DexPreoptImageLocationsOnHost []string // boot image location on host (file path without the arch subdirectory) + DexPreoptImageLocationsOnDevice []string // boot image location on device (file path without the arch subdirectory) PreoptBootClassPathDexFiles android.Paths // file paths of boot class path files PreoptBootClassPathDexLocations []string // virtual locations of boot class path files @@ -370,6 +372,15 @@ func (d dex2oatDependencyTag) ExcludeFromVisibilityEnforcement() { func (d dex2oatDependencyTag) ExcludeFromApexContents() { } +func (d dex2oatDependencyTag) AllowDisabledModuleDependency(target android.Module) bool { + // RegisterToolDeps may run after the prebuilt mutators and hence register a + // dependency on the source module even when the prebuilt is to be used. + // dex2oatPathFromDep takes that into account when it retrieves the path to + // the binary, but we also need to disable the check for dependencies on + // disabled modules. + return target.IsReplacedByPrebuilt() +} + // Dex2oatDepTag represents the dependency onto the dex2oatd module. It is added to any module that // needs dexpreopting and so it makes no sense for it to be checked for visibility or included in // the apex. @@ -377,6 +388,7 @@ var Dex2oatDepTag = dex2oatDependencyTag{} var _ android.ExcludeFromVisibilityEnforcementTag = Dex2oatDepTag var _ android.ExcludeFromApexContentsTag = Dex2oatDepTag +var _ android.AllowDisabledModuleDependency = Dex2oatDepTag // RegisterToolDeps adds the necessary dependencies to binary modules for tools // that are required later when Get(Cached)GlobalSoongConfig is called. It diff --git a/dexpreopt/dexpreopt.go b/dexpreopt/dexpreopt.go index 9d9234ff4..da015a389 100644 --- a/dexpreopt/dexpreopt.go +++ b/dexpreopt/dexpreopt.go @@ -499,11 +499,16 @@ func odexOnSystemOther(module *ModuleConfig, global *GlobalConfig) bool { // PathToLocation converts .../system/framework/arm64/boot.art to .../system/framework/boot.art func PathToLocation(path android.Path, arch android.ArchType) string { - pathArch := filepath.Base(filepath.Dir(path.String())) + return PathStringToLocation(path.String(), arch) +} + +// PathStringToLocation converts .../system/framework/arm64/boot.art to .../system/framework/boot.art +func PathStringToLocation(path string, arch android.ArchType) string { + pathArch := filepath.Base(filepath.Dir(path)) if pathArch != arch.String() { panic(fmt.Errorf("last directory in %q must be %q", path, arch.String())) } - return filepath.Join(filepath.Dir(filepath.Dir(path.String())), filepath.Base(path.String())) + return filepath.Join(filepath.Dir(filepath.Dir(path)), filepath.Base(path)) } func makefileMatch(pattern, s string) bool { diff --git a/dexpreopt/dexpreopt_gen/dexpreopt_gen.go b/dexpreopt/dexpreopt_gen/dexpreopt_gen.go index 32c4f845b..7dbe74c27 100644 --- a/dexpreopt/dexpreopt_gen/dexpreopt_gen.go +++ b/dexpreopt/dexpreopt_gen/dexpreopt_gen.go @@ -37,6 +37,12 @@ var ( globalConfigPath = flag.String("global", "", "path to global configuration file") moduleConfigPath = flag.String("module", "", "path to module configuration file") outDir = flag.String("out_dir", "", "path to output directory") + // If uses_target_files is true, dexpreopt_gen will be running on extracted target_files.zip files. + // In this case, the tool replace output file path with $(basePath)/$(on-device file path). + // The flag is useful when running dex2oat on system image and vendor image which are built separately. + usesTargetFiles = flag.Bool("uses_target_files", false, "whether or not dexpreopt is running on target_files") + // basePath indicates the path where target_files.zip is extracted. + basePath = flag.String("base_path", ".", "base path where images and tools are extracted") ) type builderContext struct { @@ -134,32 +140,28 @@ func main() { } } }() - + if *usesTargetFiles { + moduleConfig.ManifestPath = android.OptionalPath{} + prefix := "dex2oat_result" + moduleConfig.BuildPath = android.PathForOutput(ctx, filepath.Join(prefix, moduleConfig.DexLocation)) + for i, location := range moduleConfig.PreoptBootClassPathDexLocations { + moduleConfig.PreoptBootClassPathDexFiles[i] = android.PathForSource(ctx, *basePath+location) + } + for i := range moduleConfig.ClassLoaderContexts { + for _, v := range moduleConfig.ClassLoaderContexts[i] { + v.Host = android.PathForSource(ctx, *basePath+v.Device) + } + } + moduleConfig.EnforceUsesLibraries = false + for i, location := range moduleConfig.DexPreoptImageLocationsOnDevice { + moduleConfig.DexPreoptImageLocationsOnHost[i] = *basePath + location + } + } writeScripts(ctx, globalSoongConfig, globalConfig, moduleConfig, *dexpreoptScriptPath) } func writeScripts(ctx android.BuilderContext, globalSoong *dexpreopt.GlobalSoongConfig, global *dexpreopt.GlobalConfig, module *dexpreopt.ModuleConfig, dexpreoptScriptPath string) { - dexpreoptRule, err := dexpreopt.GenerateDexpreoptRule(ctx, globalSoong, global, module) - if err != nil { - panic(err) - } - - installDir := module.BuildPath.InSameDir(ctx, "dexpreopt_install") - - dexpreoptRule.Command().FlagWithArg("rm -rf ", installDir.String()) - dexpreoptRule.Command().FlagWithArg("mkdir -p ", installDir.String()) - - for _, install := range dexpreoptRule.Installs() { - installPath := installDir.Join(ctx, strings.TrimPrefix(install.To, "/")) - dexpreoptRule.Command().Text("mkdir -p").Flag(filepath.Dir(installPath.String())) - dexpreoptRule.Command().Text("cp -f").Input(install.From).Output(installPath) - } - dexpreoptRule.Command().Tool(globalSoong.SoongZip). - FlagWithArg("-o ", "$2"). - FlagWithArg("-C ", installDir.String()). - FlagWithArg("-D ", installDir.String()) - write := func(rule *android.RuleBuilder, file string) { script := &bytes.Buffer{} script.WriteString(scriptHeader) @@ -195,6 +197,30 @@ func writeScripts(ctx android.BuilderContext, globalSoong *dexpreopt.GlobalSoong panic(err) } } + dexpreoptRule, err := dexpreopt.GenerateDexpreoptRule(ctx, globalSoong, global, module) + if err != nil { + panic(err) + } + // When usesTargetFiles is true, only odex/vdex files are necessary. + // So skip redunant processes(such as copying the result to the artifact path, and zipping, and so on.) + if *usesTargetFiles { + write(dexpreoptRule, dexpreoptScriptPath) + return + } + installDir := module.BuildPath.InSameDir(ctx, "dexpreopt_install") + + dexpreoptRule.Command().FlagWithArg("rm -rf ", installDir.String()) + dexpreoptRule.Command().FlagWithArg("mkdir -p ", installDir.String()) + + for _, install := range dexpreoptRule.Installs() { + installPath := installDir.Join(ctx, strings.TrimPrefix(install.To, "/")) + dexpreoptRule.Command().Text("mkdir -p").Flag(filepath.Dir(installPath.String())) + dexpreoptRule.Command().Text("cp -f").Input(install.From).Output(installPath) + } + dexpreoptRule.Command().Tool(globalSoong.SoongZip). + FlagWithArg("-o ", "$2"). + FlagWithArg("-C ", installDir.String()). + FlagWithArg("-D ", installDir.String()) // The written scripts will assume the input is $1 and the output is $2 if module.DexPath.String() != "$1" { diff --git a/filesystem/system_image.go b/filesystem/system_image.go index a7c914395..1d24d6d41 100644 --- a/filesystem/system_image.go +++ b/filesystem/system_image.go @@ -50,17 +50,19 @@ func (s *systemImage) buildExtraFiles(ctx android.ModuleContext, root android.Ou func (s *systemImage) buildLinkerConfigFile(ctx android.ModuleContext, root android.OutputPath) android.OutputPath { input := android.PathForModuleSrc(ctx, android.String(s.properties.Linker_config_src)) output := root.Join(ctx, "system", "etc", "linker.config.pb") + + // we need "Module"s for packaging items var otherModules []android.Module + deps := s.GatherPackagingSpecs(ctx) ctx.WalkDeps(func(child, parent android.Module) bool { - // Don't track direct dependencies that aren't not packaged - if parent == s { - if pi, ok := ctx.OtherModuleDependencyTag(child).(android.PackagingItem); !ok || !pi.IsPackagingItem() { - return false + for _, ps := range child.PackagingSpecs() { + if _, ok := deps[ps.RelPathInPackage()]; ok { + otherModules = append(otherModules, child) } } - otherModules = append(otherModules, child) return true }) + builder := android.NewRuleBuilder(pctx, ctx) linkerconfig.BuildLinkerConfig(ctx, builder, input, otherModules, output) builder.Build("conv_linker_config", "Generate linker config protobuf "+output.String()) diff --git a/java/Android.bp b/java/Android.bp index 623a6c577..680f3a17c 100644 --- a/java/Android.bp +++ b/java/Android.bp @@ -45,6 +45,7 @@ bootstrap_go_package { "genrule.go", "hiddenapi.go", "hiddenapi_modular.go", + "hiddenapi_monolithic.go", "hiddenapi_singleton.go", "jacoco.go", "java.go", diff --git a/java/app.go b/java/app.go index 5695022f9..fc1ace07b 100755 --- a/java/app.go +++ b/java/app.go @@ -893,7 +893,7 @@ func (a *AndroidApp) IsNativeCoverageNeeded(ctx android.BaseModuleContext) bool return ctx.Device() && ctx.DeviceConfig().NativeCoverageEnabled() } -func (a *AndroidApp) PreventInstall() { +func (a *AndroidApp) SetPreventInstall() { a.appProperties.PreventInstall = true } diff --git a/java/app_import.go b/java/app_import.go index 839051ea8..6fe620407 100644 --- a/java/app_import.go +++ b/java/app_import.go @@ -99,6 +99,9 @@ type AndroidAppImportProperties struct { // If set, create package-export.apk, which other packages can // use to get PRODUCT-agnostic resource data like IDs and type definitions. Export_package_resources *bool + + // Optional. Install to a subdirectory of the default install path for the module + Relative_install_path *string } func (a *AndroidAppImport) IsInstallable() bool { @@ -263,20 +266,25 @@ func (a *AndroidAppImport) generateAndroidBuildActions(ctx android.ModuleContext jnisUncompressed := android.PathForModuleOut(ctx, "jnis-uncompressed", ctx.ModuleName()+".apk") a.uncompressEmbeddedJniLibs(ctx, srcApk, jnisUncompressed.OutputPath) - var installDir android.InstallPath + var pathFragments []string + relInstallPath := String(a.properties.Relative_install_path) if a.isPrebuiltFrameworkRes() { // framework-res.apk is installed as system/framework/framework-res.apk - installDir = android.PathForModuleInstall(ctx, "framework") + if relInstallPath != "" { + ctx.PropertyErrorf("relative_install_path", "Relative_install_path cannot be set for framework-res") + } + pathFragments = []string{"framework"} a.preprocessed = true } else if Bool(a.properties.Privileged) { - installDir = android.PathForModuleInstall(ctx, "priv-app", a.BaseModuleName()) + pathFragments = []string{"priv-app", relInstallPath, a.BaseModuleName()} } else if ctx.InstallInTestcases() { - installDir = android.PathForModuleInstall(ctx, a.BaseModuleName(), ctx.DeviceConfig().DeviceArch()) + pathFragments = []string{relInstallPath, a.BaseModuleName(), ctx.DeviceConfig().DeviceArch()} } else { - installDir = android.PathForModuleInstall(ctx, "app", a.BaseModuleName()) + pathFragments = []string{"app", relInstallPath, a.BaseModuleName()} } + installDir := android.PathForModuleInstall(ctx, pathFragments...) a.dexpreopter.isApp = true a.dexpreopter.installPath = installDir.Join(ctx, a.BaseModuleName()+".apk") a.dexpreopter.isPresignedPrebuilt = Bool(a.properties.Presigned) diff --git a/java/app_import_test.go b/java/app_import_test.go index 147ae45bb..024a3df51 100644 --- a/java/app_import_test.go +++ b/java/app_import_test.go @@ -493,6 +493,69 @@ func TestAndroidAppImport_frameworkRes(t *testing.T) { } } +func TestAndroidAppImport_relativeInstallPath(t *testing.T) { + bp := ` + android_app_import { + name: "no_relative_install_path", + apk: "prebuilts/apk/app.apk", + presigned: true, + } + + android_app_import { + name: "relative_install_path", + apk: "prebuilts/apk/app.apk", + presigned: true, + relative_install_path: "my/path", + } + + android_app_import { + name: "framework-res", + apk: "prebuilts/apk/app.apk", + presigned: true, + prefer: true, + } + + android_app_import { + name: "privileged_relative_install_path", + apk: "prebuilts/apk/app.apk", + presigned: true, + privileged: true, + relative_install_path: "my/path" + } + ` + testCases := []struct { + name string + expectedInstallPath string + errorMessage string + }{ + { + name: "no_relative_install_path", + expectedInstallPath: "out/soong/target/product/test_device/system/app/no_relative_install_path/no_relative_install_path.apk", + errorMessage: "Install path is not correct when relative_install_path is missing", + }, + { + name: "relative_install_path", + expectedInstallPath: "out/soong/target/product/test_device/system/app/my/path/relative_install_path/relative_install_path.apk", + errorMessage: "Install path is not correct for app when relative_install_path is present", + }, + { + name: "prebuilt_framework-res", + expectedInstallPath: "out/soong/target/product/test_device/system/framework/framework-res.apk", + errorMessage: "Install path is not correct for framework-res", + }, + { + name: "privileged_relative_install_path", + expectedInstallPath: "out/soong/target/product/test_device/system/priv-app/my/path/privileged_relative_install_path/privileged_relative_install_path.apk", + errorMessage: "Install path is not correct for privileged app when relative_install_path is present", + }, + } + for _, testCase := range testCases { + ctx, _ := testJava(t, bp) + mod := ctx.ModuleForTests(testCase.name, "android_common").Module().(*AndroidAppImport) + android.AssertPathRelativeToTopEquals(t, testCase.errorMessage, testCase.expectedInstallPath, mod.installPath) + } +} + func TestAndroidTestImport(t *testing.T) { ctx, _ := testJava(t, ` android_test_import { diff --git a/java/base.go b/java/base.go index 19c85cd75..f7989b8a2 100644 --- a/java/base.go +++ b/java/base.go @@ -229,12 +229,6 @@ type DeviceProperties struct { // otherwise provides defaults libraries to add to the bootclasspath. System_modules *string - // The name of the module as used in build configuration. - // - // Allows a library to separate its actual name from the name used in - // build configuration, e.g.ctx.Config().BootJars(). - ConfigurationName *string `blueprint:"mutated"` - // set the name of the output Stem *string @@ -895,8 +889,8 @@ func (j *Module) compile(ctx android.ModuleContext, aaptSrcJar android.Path) { kotlincFlags := j.properties.Kotlincflags CheckKotlincFlags(ctx, kotlincFlags) - // Dogfood the JVM_IR backend. - kotlincFlags = append(kotlincFlags, "-Xuse-ir") + // Workaround for KT-46512 + kotlincFlags = append(kotlincFlags, "-Xsam-conversions=class") // If there are kotlin files, compile them first but pass all the kotlin and java files // kotlinc will use the java files to resolve types referenced by the kotlin files, but @@ -1177,8 +1171,14 @@ func (j *Module) compile(ctx android.ModuleContext, aaptSrcJar android.Path) { j.properties.Instrument = true } + // enforce syntax check to jacoco filters for any build (http://b/183622051) + specs := j.jacocoModuleToZipCommand(ctx) + if ctx.Failed() { + return + } + if j.shouldInstrument(ctx) { - outputFile = j.instrument(ctx, flags, outputFile, jarName) + outputFile = j.instrument(ctx, flags, outputFile, jarName, specs) } // merge implementation jar with resources if necessary @@ -1217,10 +1217,6 @@ func (j *Module) compile(ctx android.ModuleContext, aaptSrcJar android.Path) { return } - // Hidden API CSV generation and dex encoding - dexOutputFile = j.hiddenAPIExtractAndEncode(ctx, dexOutputFile, j.implementationJarFile, - proptools.Bool(j.dexProperties.Uncompress_dex)) - // merge dex jar with resources if necessary if j.resourceJar != nil { jars := android.Paths{dexOutputFile, j.resourceJar} @@ -1236,6 +1232,12 @@ func (j *Module) compile(ctx android.ModuleContext, aaptSrcJar android.Path) { } } + // Initialize the hiddenapi structure. + j.initHiddenAPI(ctx, dexOutputFile, j.implementationJarFile, j.dexProperties.Uncompress_dex) + + // Encode hidden API flags in dex file, if needed. + dexOutputFile = j.hiddenAPIEncodeDex(ctx, dexOutputFile) + j.dexJarFile = dexOutputFile // Dexpreopting @@ -1390,9 +1392,7 @@ func (j *Module) compileJavaHeader(ctx android.ModuleContext, srcFiles, srcJars } func (j *Module) instrument(ctx android.ModuleContext, flags javaBuilderFlags, - classesJar android.Path, jarName string) android.OutputPath { - - specs := j.jacocoModuleToZipCommand(ctx) + classesJar android.Path, jarName string, specs string) android.OutputPath { jacocoReportClassesFile := android.PathForModuleOut(ctx, "jacoco-report-classes", jarName) instrumentedJar := android.PathForModuleOut(ctx, "jacoco", jarName).OutputPath @@ -1495,15 +1495,6 @@ func (j *Module) Stem() string { return proptools.StringDefault(j.deviceProperties.Stem, j.Name()) } -// ConfigurationName returns the name of the module as used in build configuration. -// -// This is usually the same as BaseModuleName() except for the <x>.impl libraries created by -// java_sdk_library in which case this is the BaseModuleName() without the ".impl" suffix, -// i.e. just <x>. -func (j *Module) ConfigurationName() string { - return proptools.StringDefault(j.deviceProperties.ConfigurationName, j.BaseModuleName()) -} - func (j *Module) JacocoReportClassesFile() android.Path { return j.jacocoReportClassesFile } diff --git a/java/boot_jars.go b/java/boot_jars.go index 1fb3deb0a..7abda8031 100644 --- a/java/boot_jars.go +++ b/java/boot_jars.go @@ -89,7 +89,7 @@ func (b *bootJarsSingleton) GenerateBuildActions(ctx android.SingletonContext) { name := android.RemoveOptionalPrebuiltPrefix(ctx.ModuleName(module)) if apex, ok := moduleToApex[name]; ok { apexInfo := ctx.ModuleProvider(module, android.ApexInfoProvider).(android.ApexInfo) - if (apex == "platform" && apexInfo.IsForPlatform()) || apexInfo.InApexByBaseName(apex) { + if (apex == "platform" && apexInfo.IsForPlatform()) || apexInfo.InApexModule(apex) { // The module name/apex variant should be unique in the system but double check // just in case something has gone wrong. if existing, ok := nameToApexVariant[name]; ok { diff --git a/java/bootclasspath.go b/java/bootclasspath.go index 02833ab66..eddcc838b 100644 --- a/java/bootclasspath.go +++ b/java/bootclasspath.go @@ -29,7 +29,7 @@ func init() { func registerBootclasspathBuildComponents(ctx android.RegistrationContext) { ctx.FinalDepsMutators(func(ctx android.RegisterMutatorsContext) { - ctx.BottomUp("bootclasspath_deps", bootclasspathDepsMutator) + ctx.BottomUp("bootclasspath_deps", bootclasspathDepsMutator).Parallel() }) } @@ -95,6 +95,15 @@ func addDependencyOntoApexModulePair(ctx android.BottomUpMutatorContext, apex st if ctx.OtherModuleDependencyVariantExists(variations, prebuiltName) { ctx.AddVariationDependencies(variations, tag, prebuiltName) addedDep = true + } else if ctx.Config().AlwaysUsePrebuiltSdks() && len(variations) > 0 { + // TODO(b/179354495): Remove this code path once the Android build has been fully migrated to + // use bootclasspath_fragment properly. + // Some prebuilt java_sdk_library modules do not yet have an APEX variations so try and add a + // dependency on the non-APEX variant. + if ctx.OtherModuleDependencyVariantExists(nil, prebuiltName) { + ctx.AddVariationDependencies(nil, tag, prebuiltName) + addedDep = true + } } // If no appropriate variant existing for this, so no dependency could be added, then it is an @@ -226,12 +235,3 @@ func (p BootclasspathAPIProperties) sdkKindToStubLibs() map[android.SdkKind][]st m[android.SdkCorePlatform] = p.Core_platform_api.Stub_libs return m } - -// bootclasspathApiInfo contains paths resolved from BootclasspathAPIProperties -type bootclasspathApiInfo struct { - // stubJarsByKind maps from the android.SdkKind to the paths containing dex stub jars for each - // kind. - stubJarsByKind map[android.SdkKind]android.Paths -} - -var bootclasspathApiInfoProvider = blueprint.NewProvider(bootclasspathApiInfo{}) diff --git a/java/bootclasspath_fragment.go b/java/bootclasspath_fragment.go index bed629da3..db49df8df 100644 --- a/java/bootclasspath_fragment.go +++ b/java/bootclasspath_fragment.go @@ -61,8 +61,14 @@ func (b bootclasspathFragmentContentDependencyTag) ReplaceSourceWithPrebuilt() b } // SdkMemberType causes dependencies added with this tag to be automatically added to the sdk as if -// they were specified using java_boot_libs. -func (b bootclasspathFragmentContentDependencyTag) SdkMemberType(_ android.Module) android.SdkMemberType { +// they were specified using java_boot_libs or java_sdk_libs. +func (b bootclasspathFragmentContentDependencyTag) SdkMemberType(child android.Module) android.SdkMemberType { + // If the module is a java_sdk_library then treat it as if it was specified in the java_sdk_libs + // property, otherwise treat if it was specified in the java_boot_libs property. + if javaSdkLibrarySdkMemberType.IsInstance(child) { + return javaSdkLibrarySdkMemberType + } + return javaBootLibsSdkMemberType } @@ -85,6 +91,9 @@ func IsBootclasspathFragmentContentDepTag(tag blueprint.DependencyTag) bool { type BootclasspathFragmentCoverageAffectedProperties struct { // The contents of this bootclasspath_fragment, could be either java_library, or java_sdk_library. // + // A java_sdk_library specified here will also be treated as if it was specified on the stub_libs + // property. + // // The order of this list matters as it is the order that is used in the bootclasspath. Contents []string @@ -103,6 +112,11 @@ type bootclasspathFragmentProperties struct { Coverage BootclasspathFragmentCoverageAffectedProperties Hidden_api HiddenAPIFlagFileProperties + + // Properties that allow a fragment to depend on other fragments. This is needed for hidden API + // processing as it needs access to all the classes used by a fragment including those provided + // by other fragments. + BootclasspathFragmentsDepsProperties } type BootclasspathFragmentModule struct { @@ -114,6 +128,17 @@ type BootclasspathFragmentModule struct { properties bootclasspathFragmentProperties } +// commonBootclasspathFragment defines the methods that are implemented by both source and prebuilt +// bootclasspath fragment modules. +type commonBootclasspathFragment interface { + // produceHiddenAPIAllFlagsFile produces the all-flags.csv and intermediate files. + // + // Updates the supplied hiddenAPIInfo with the paths to the generated files set. + produceHiddenAPIAllFlagsFile(ctx android.ModuleContext, contents []hiddenAPIModule, input HiddenAPIFlagInput) *HiddenAPIFlagOutput +} + +var _ commonBootclasspathFragment = (*BootclasspathFragmentModule)(nil) + func bootclasspathFragmentFactory() android.Module { m := &BootclasspathFragmentModule{} m.AddProperties(&m.properties) @@ -245,27 +270,15 @@ var BootclasspathFragmentApexContentInfoProvider = blueprint.NewProvider(Bootcla // BootclasspathFragmentApexContentInfo contains the bootclasspath_fragments contributions to the // apex contents. type BootclasspathFragmentApexContentInfo struct { - // ClasspathFragmentProtoOutput is an output path for the generated classpaths.proto config of this module. - // - // The file should be copied to a relevant place on device, see ClasspathFragmentProtoInstallDir - // for more details. - ClasspathFragmentProtoOutput android.OutputPath - - // ClasspathFragmentProtoInstallDir contains information about on device location for the generated classpaths.proto file. - // - // The path encodes expected sub-location within partitions, i.e. etc/classpaths/<proto-file>, - // for ClasspathFragmentProtoOutput. To get sub-location, instead of the full output / make path - // use android.InstallPath#Rel(). - // - // This is only relevant for APEX modules as they perform their own installation; while regular - // system files are installed via ClasspathFragmentBase#androidMkEntries(). - ClasspathFragmentProtoInstallDir android.InstallPath - // The image config, internal to this module (and the dex_bootjars singleton). // // Will be nil if the BootclasspathFragmentApexContentInfo has not been provided for a specific module. That can occur // when SkipDexpreoptBootJars(ctx) returns true. imageConfig *bootImageConfig + + // Map from the name of the context module (as returned by Name()) to the hidden API encoded dex + // jar path. + contentModuleDexJarPaths map[string]android.Path } func (i BootclasspathFragmentApexContentInfo) Modules() android.ConfiguredJarList { @@ -292,10 +305,14 @@ func (i BootclasspathFragmentApexContentInfo) AndroidBootImageFilesByArchType() // DexBootJarPathForContentModule returns the path to the dex boot jar for specified module. // // The dex boot jar is one which has had hidden API encoding performed on it. -func (i BootclasspathFragmentApexContentInfo) DexBootJarPathForContentModule(module android.Module) android.Path { - j := module.(UsesLibraryDependency) - dexJar := j.DexJarBuildPath() - return dexJar +func (i BootclasspathFragmentApexContentInfo) DexBootJarPathForContentModule(module android.Module) (android.Path, error) { + name := module.Name() + if dexJar, ok := i.contentModuleDexJarPaths[name]; ok { + return dexJar, nil + } else { + return nil, fmt.Errorf("unknown bootclasspath_fragment content module %s, expected one of %s", + name, strings.Join(android.SortedStringKeys(i.contentModuleDexJarPaths), ", ")) + } } func (b *BootclasspathFragmentModule) DepIsInSameApex(ctx android.BaseModuleContext, dep android.Module) bool { @@ -350,6 +367,11 @@ func (b *BootclasspathFragmentModule) DepsMutator(ctx android.BottomUpMutatorCon dexpreopt.RegisterToolDeps(ctx) } +func (b *BootclasspathFragmentModule) BootclasspathDepsMutator(ctx android.BottomUpMutatorContext) { + // Add dependencies on all the fragments. + b.properties.BootclasspathFragmentsDepsProperties.addDependenciesOntoFragments(ctx) +} + func (b *BootclasspathFragmentModule) GenerateAndroidBuildActions(ctx android.ModuleContext) { // Only perform a consistency check if this module is the active module. That will prevent an // unused prebuilt that was created without instrumentation from breaking an instrumentation @@ -361,27 +383,87 @@ func (b *BootclasspathFragmentModule) GenerateAndroidBuildActions(ctx android.Mo // Generate classpaths.proto config b.generateClasspathProtoBuildActions(ctx) + // Gather the bootclasspath fragment's contents. + var contents []android.Module + ctx.VisitDirectDeps(func(module android.Module) { + tag := ctx.OtherModuleDependencyTag(module) + if IsBootclasspathFragmentContentDepTag(tag) { + contents = append(contents, module) + } + }) + + fragments := gatherApexModulePairDepsWithTag(ctx, bootclasspathFragmentDepTag) + // Perform hidden API processing. - b.generateHiddenAPIBuildActions(ctx) + hiddenAPIFlagOutput := b.generateHiddenAPIBuildActions(ctx, contents, fragments) + + // Verify that the image_name specified on a bootclasspath_fragment is valid even if this is a + // prebuilt which will not use the image config. + imageConfig := b.getImageConfig(ctx) + + // A prebuilt fragment cannot contribute to the apex. + if !android.IsModulePrebuilt(ctx.Module()) { + // Provide the apex content info. + b.provideApexContentInfo(ctx, imageConfig, contents, hiddenAPIFlagOutput) + } +} - // Construct the boot image info from the config. +// provideApexContentInfo creates, initializes and stores the apex content info for use by other +// modules. +func (b *BootclasspathFragmentModule) provideApexContentInfo(ctx android.ModuleContext, imageConfig *bootImageConfig, contents []android.Module, hiddenAPIFlagOutput *HiddenAPIFlagOutput) { + // Construct the apex content info from the config. info := BootclasspathFragmentApexContentInfo{ - ClasspathFragmentProtoInstallDir: b.classpathFragmentBase().installDirPath, - ClasspathFragmentProtoOutput: b.classpathFragmentBase().outputFilepath, - imageConfig: nil, + imageConfig: imageConfig, } + // Populate the apex content info with paths to the dex jars. + b.populateApexContentInfoDexJars(ctx, &info, contents, hiddenAPIFlagOutput) + if !SkipDexpreoptBootJars(ctx) { // Force the GlobalSoongConfig to be created and cached for use by the dex_bootjars // GenerateSingletonBuildActions method as it cannot create it for itself. dexpreopt.GetGlobalSoongConfig(ctx) - info.imageConfig = b.getImageConfig(ctx) + + // Only generate the boot image if the configuration does not skip it. + b.generateBootImageBuildActions(ctx, contents) } - // Make it available for other modules. + // Make the apex content info available for other modules. ctx.SetProvider(BootclasspathFragmentApexContentInfoProvider, info) } +// populateApexContentInfoDexJars adds paths to the dex jars provided by this fragment to the +// apex content info. +func (b *BootclasspathFragmentModule) populateApexContentInfoDexJars(ctx android.ModuleContext, info *BootclasspathFragmentApexContentInfo, contents []android.Module, hiddenAPIFlagOutput *HiddenAPIFlagOutput) { + + info.contentModuleDexJarPaths = map[string]android.Path{} + if hiddenAPIFlagOutput != nil { + // Hidden API encoding has been performed. + flags := hiddenAPIFlagOutput.AllFlagsPath + for _, m := range contents { + h := m.(hiddenAPIModule) + unencodedDex := h.bootDexJar() + if unencodedDex == nil { + // This is an error. Sometimes Soong will report the error directly, other times it will + // defer the error reporting to happen only when trying to use the missing file in ninja. + // Either way it is handled by extractBootDexJarsFromHiddenAPIModules which must have been + // called before this as it generates the flags that are used to encode these files. + continue + } + + outputDir := android.PathForModuleOut(ctx, "hiddenapi-modular/encoded").OutputPath + encodedDex := hiddenAPIEncodeDex(ctx, unencodedDex, flags, *h.uncompressDex(), outputDir) + info.contentModuleDexJarPaths[m.Name()] = encodedDex + } + } else { + for _, m := range contents { + j := m.(UsesLibraryDependency) + dexJar := j.DexJarBuildPath() + info.contentModuleDexJarPaths[m.Name()] = dexJar + } + } +} + // generateClasspathProtoBuildActions generates all required build actions for classpath.proto config func (b *BootclasspathFragmentModule) generateClasspathProtoBuildActions(ctx android.ModuleContext) { var classpathJars []classpathJar @@ -395,8 +477,16 @@ func (b *BootclasspathFragmentModule) generateClasspathProtoBuildActions(ctx and } func (b *BootclasspathFragmentModule) ClasspathFragmentToConfiguredJarList(ctx android.ModuleContext) android.ConfiguredJarList { - // TODO(satayev): populate with actual content - return android.EmptyConfiguredJarList() + if "art" == proptools.String(b.properties.Image_name) { + return b.getImageConfig(ctx).modules + } + + global := dexpreopt.GetGlobalConfig(ctx) + + // Only create configs for updatable boot jars. Non-updatable boot jars must be part of the + // platform_bootclasspath's classpath proto config to guarantee that they come before any + // updatable jars at runtime. + return global.UpdatableBootJars.Filter(b.properties.Contents) } func (b *BootclasspathFragmentModule) getImageConfig(ctx android.EarlyModuleContext) *bootImageConfig { @@ -419,19 +509,120 @@ func (b *BootclasspathFragmentModule) getImageConfig(ctx android.EarlyModuleCont } // generateHiddenAPIBuildActions generates all the hidden API related build rules. -func (b *BootclasspathFragmentModule) generateHiddenAPIBuildActions(ctx android.ModuleContext) { - // Resolve the properties to paths. - flagFileInfo := b.properties.Hidden_api.hiddenAPIFlagFileInfo(ctx) +func (b *BootclasspathFragmentModule) generateHiddenAPIBuildActions(ctx android.ModuleContext, contents []android.Module, fragments []android.Module) *HiddenAPIFlagOutput { + + // Create hidden API input structure. + input := b.createHiddenAPIFlagInput(ctx, contents, fragments) + + var output *HiddenAPIFlagOutput + + // Hidden API processing is conditional as a temporary workaround as not all + // bootclasspath_fragments provide the appropriate information needed for hidden API processing + // which leads to breakages of the build. + // TODO(b/179354495): Stop hidden API processing being conditional once all bootclasspath_fragment + // modules have been updated to support it. + if input.canPerformHiddenAPIProcessing(ctx, b.properties) { + // Get the content modules that contribute to the hidden API processing. + hiddenAPIModules := gatherHiddenAPIModuleFromContents(ctx, contents) + + // Delegate the production of the hidden API all-flags.csv file to a module type specific method. + common := ctx.Module().(commonBootclasspathFragment) + output = common.produceHiddenAPIAllFlagsFile(ctx, hiddenAPIModules, input) + } + + // Initialize a HiddenAPIInfo structure. + hiddenAPIInfo := HiddenAPIInfo{ + // The monolithic hidden API processing needs access to the flag files that override the default + // flags from all the fragments whether or not they actually perform their own hidden API flag + // generation. That is because the monolithic hidden API processing uses those flag files to + // perform its own flag generation. + FlagFilesByCategory: input.FlagFilesByCategory, + + // Other bootclasspath_fragments that depend on this need the transitive set of stub dex jars + // from this to resolve any references from their code to classes provided by this fragment + // and the fragments this depends upon. + TransitiveStubDexJarsByKind: input.transitiveStubDexJarsByKind(), + } + + if output != nil { + // The monolithic hidden API processing also needs access to all the output files produced by + // hidden API processing of this fragment. + hiddenAPIInfo.HiddenAPIFlagOutput = *output + } + + // Provide it for use by other modules. + ctx.SetProvider(HiddenAPIInfoProvider, hiddenAPIInfo) + + return output +} - // Store the information for use by platform_bootclasspath. - ctx.SetProvider(hiddenAPIFlagFileInfoProvider, flagFileInfo) +// createHiddenAPIFlagInput creates a HiddenAPIFlagInput struct and initializes it with information derived +// from the properties on this module and its dependencies. +func (b *BootclasspathFragmentModule) createHiddenAPIFlagInput(ctx android.ModuleContext, contents []android.Module, fragments []android.Module) HiddenAPIFlagInput { - // Convert the kind specific lists of modules into kind specific lists of jars. - stubJarsByKind := hiddenAPIGatherStubLibDexJarPaths(ctx) + // Merge the HiddenAPIInfo from all the fragment dependencies. + dependencyHiddenApiInfo := newHiddenAPIInfo() + dependencyHiddenApiInfo.mergeFromFragmentDeps(ctx, fragments) - // Store the information for use by other modules. - bootclasspathApiInfo := bootclasspathApiInfo{stubJarsByKind: stubJarsByKind} - ctx.SetProvider(bootclasspathApiInfoProvider, bootclasspathApiInfo) + // Create hidden API flag input structure. + input := newHiddenAPIFlagInput() + + // Update the input structure with information obtained from the stub libraries. + input.gatherStubLibInfo(ctx, contents) + + // Populate with flag file paths from the properties. + input.extractFlagFilesFromProperties(ctx, &b.properties.Hidden_api) + + // Store the stub dex jars from this module's fragment dependencies. + input.DependencyStubDexJarsByKind = dependencyHiddenApiInfo.TransitiveStubDexJarsByKind + + return input +} + +// produceHiddenAPIAllFlagsFile produces the hidden API all-flags.csv file (and supporting files) +// for the fragment. +func (b *BootclasspathFragmentModule) produceHiddenAPIAllFlagsFile(ctx android.ModuleContext, contents []hiddenAPIModule, input HiddenAPIFlagInput) *HiddenAPIFlagOutput { + // Generate the rules to create the hidden API flags and update the supplied hiddenAPIInfo with the + // paths to the created files. + return hiddenAPIGenerateAllFlagsForBootclasspathFragment(ctx, contents, input) +} + +// generateBootImageBuildActions generates ninja rules to create the boot image if required for this +// module. +func (b *BootclasspathFragmentModule) generateBootImageBuildActions(ctx android.ModuleContext, contents []android.Module) { + global := dexpreopt.GetGlobalConfig(ctx) + if !shouldBuildBootImages(ctx.Config(), global) { + return + } + + // Bootclasspath fragment modules that are not preferred do not produce a boot image. + if !isActiveModule(ctx.Module()) { + return + } + + // Bootclasspath fragment modules that have no image_name property do not produce a boot image. + imageConfig := b.getImageConfig(ctx) + if imageConfig == nil { + return + } + + // Bootclasspath fragment modules that are for the platform do not produce a boot image. + apexInfo := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo) + if apexInfo.IsForPlatform() { + return + } + + // Bootclasspath fragment modules that are versioned do not produce a boot image. + if android.IsModuleInVersionedSdk(ctx.Module()) { + return + } + + // Copy the dex jars of this fragment's content modules to their predefined locations. + copyBootJarsToPredefinedLocations(ctx, contents, imageConfig.modules, imageConfig.dexPaths) + + // Build a profile for the image config and then use that to build the boot image. + profile := bootImageProfileRule(ctx, imageConfig) + buildBootImage(ctx, imageConfig, profile) } type bootclasspathFragmentMemberType struct { @@ -473,7 +664,22 @@ type bootclasspathFragmentSdkMemberProperties struct { Core_platform_stub_libs []string // Flag files by *hiddenAPIFlagFileCategory - Flag_files_by_category map[*hiddenAPIFlagFileCategory]android.Paths + Flag_files_by_category FlagFilesByCategory + + // The path to the generated stub-flags.csv file. + Stub_flags_path android.OptionalPath + + // The path to the generated annotation-flags.csv file. + Annotation_flags_path android.OptionalPath + + // The path to the generated metadata.csv file. + Metadata_path android.OptionalPath + + // The path to the generated index.csv file. + Index_path android.OptionalPath + + // The path to the generated all-flags.csv file. + All_flags_path android.OptionalPath } func (b *bootclasspathFragmentSdkMemberProperties) PopulateFromVariant(ctx android.SdkMemberContext, variant android.Module) { @@ -482,10 +688,17 @@ func (b *bootclasspathFragmentSdkMemberProperties) PopulateFromVariant(ctx andro b.Image_name = module.properties.Image_name b.Contents = module.properties.Contents - // Get the flag file information from the module. + // Get the hidden API information from the module. mctx := ctx.SdkModuleContext() - flagFileInfo := mctx.OtherModuleProvider(module, hiddenAPIFlagFileInfoProvider).(hiddenAPIFlagFileInfo) - b.Flag_files_by_category = flagFileInfo.categoryToPaths + hiddenAPIInfo := mctx.OtherModuleProvider(module, HiddenAPIInfoProvider).(HiddenAPIInfo) + b.Flag_files_by_category = hiddenAPIInfo.FlagFilesByCategory + + // Copy all the generated file paths. + b.Stub_flags_path = android.OptionalPathForPath(hiddenAPIInfo.StubFlagsPath) + b.Annotation_flags_path = android.OptionalPathForPath(hiddenAPIInfo.AnnotationFlagsPath) + b.Metadata_path = android.OptionalPathForPath(hiddenAPIInfo.MetadataPath) + b.Index_path = android.OptionalPathForPath(hiddenAPIInfo.IndexPath) + b.All_flags_path = android.OptionalPathForPath(hiddenAPIInfo.AllFlagsPath) // Copy stub_libs properties. b.Stub_libs = module.properties.Api.Stub_libs @@ -513,14 +726,17 @@ func (b *bootclasspathFragmentSdkMemberProperties) AddToPropertySet(ctx android. corePlatformApiPropertySet.AddPropertyWithTag("stub_libs", b.Core_platform_stub_libs, requiredMemberDependency) } + hiddenAPISet := propertySet.AddPropertySet("hidden_api") + hiddenAPIDir := "hiddenapi" + + // Copy manually curated flag files specified on the bootclasspath_fragment. if b.Flag_files_by_category != nil { - hiddenAPISet := propertySet.AddPropertySet("hidden_api") for _, category := range hiddenAPIFlagFileCategories { paths := b.Flag_files_by_category[category] if len(paths) > 0 { dests := []string{} for _, p := range paths { - dest := filepath.Join("hiddenapi", p.Base()) + dest := filepath.Join(hiddenAPIDir, p.Base()) builder.CopyToSnapshot(p, dest) dests = append(dests, dest) } @@ -528,10 +744,47 @@ func (b *bootclasspathFragmentSdkMemberProperties) AddToPropertySet(ctx android. } } } + + copyOptionalPath := func(path android.OptionalPath, property string) { + if path.Valid() { + p := path.Path() + dest := filepath.Join(hiddenAPIDir, p.Base()) + builder.CopyToSnapshot(p, dest) + hiddenAPISet.AddProperty(property, dest) + } + } + + // Copy all the generated files, if available. + copyOptionalPath(b.Stub_flags_path, "stub_flags") + copyOptionalPath(b.Annotation_flags_path, "annotation_flags") + copyOptionalPath(b.Metadata_path, "metadata") + copyOptionalPath(b.Index_path, "index") + copyOptionalPath(b.All_flags_path, "all_flags") } var _ android.SdkMemberType = (*bootclasspathFragmentMemberType)(nil) +// prebuiltBootclasspathFragmentProperties contains additional prebuilt_bootclasspath_fragment +// specific properties. +type prebuiltBootclasspathFragmentProperties struct { + Hidden_api struct { + // The path to the stub-flags.csv file created by the bootclasspath_fragment. + Stub_flags *string `android:"path"` + + // The path to the annotation-flags.csv file created by the bootclasspath_fragment. + Annotation_flags *string `android:"path"` + + // The path to the metadata.csv file created by the bootclasspath_fragment. + Metadata *string `android:"path"` + + // The path to the index.csv file created by the bootclasspath_fragment. + Index *string `android:"path"` + + // The path to the all-flags.csv file created by the bootclasspath_fragment. + All_flags *string `android:"path"` + } +} + // A prebuilt version of the bootclasspath_fragment module. // // At the moment this is basically just a bootclasspath_fragment module that can be used as a @@ -540,6 +793,9 @@ var _ android.SdkMemberType = (*bootclasspathFragmentMemberType)(nil) type prebuiltBootclasspathFragmentModule struct { BootclasspathFragmentModule prebuilt android.Prebuilt + + // Additional prebuilt specific properties. + prebuiltProperties prebuiltBootclasspathFragmentProperties } func (module *prebuiltBootclasspathFragmentModule) Prebuilt() *android.Prebuilt { @@ -550,9 +806,33 @@ func (module *prebuiltBootclasspathFragmentModule) Name() string { return module.prebuilt.Name(module.ModuleBase.Name()) } +// produceHiddenAPIAllFlagsFile returns a path to the prebuilt all-flags.csv or nil if none is +// specified. +func (module *prebuiltBootclasspathFragmentModule) produceHiddenAPIAllFlagsFile(ctx android.ModuleContext, contents []hiddenAPIModule, _ HiddenAPIFlagInput) *HiddenAPIFlagOutput { + pathForOptionalSrc := func(src *string) android.Path { + if src == nil { + // TODO(b/179354495): Fail if this is not provided once prebuilts have been updated. + return nil + } + return android.PathForModuleSrc(ctx, *src) + } + + output := HiddenAPIFlagOutput{ + StubFlagsPath: pathForOptionalSrc(module.prebuiltProperties.Hidden_api.Stub_flags), + AnnotationFlagsPath: pathForOptionalSrc(module.prebuiltProperties.Hidden_api.Annotation_flags), + MetadataPath: pathForOptionalSrc(module.prebuiltProperties.Hidden_api.Metadata), + IndexPath: pathForOptionalSrc(module.prebuiltProperties.Hidden_api.Index), + AllFlagsPath: pathForOptionalSrc(module.prebuiltProperties.Hidden_api.All_flags), + } + + return &output +} + +var _ commonBootclasspathFragment = (*prebuiltBootclasspathFragmentModule)(nil) + func prebuiltBootclasspathFragmentFactory() android.Module { m := &prebuiltBootclasspathFragmentModule{} - m.AddProperties(&m.properties) + m.AddProperties(&m.properties, &m.prebuiltProperties) // This doesn't actually have any prebuilt files of its own so pass a placeholder for the srcs // array. android.InitPrebuiltModule(m, &[]string{"placeholder"}) diff --git a/java/bootclasspath_fragment_test.go b/java/bootclasspath_fragment_test.go index 32ed7ea81..581625d8c 100644 --- a/java/bootclasspath_fragment_test.go +++ b/java/bootclasspath_fragment_test.go @@ -204,7 +204,7 @@ func TestBootclasspathFragment_StubLibs(t *testing.T) { result := android.GroupFixturePreparers( prepareForTestWithBootclasspathFragment, PrepareForTestWithJavaSdkLibraryFiles, - FixtureWithLastReleaseApis("mysdklibrary", "mycoreplatform"), + FixtureWithLastReleaseApis("mysdklibrary", "myothersdklibrary", "mycoreplatform"), ).RunTestWithBp(t, ` bootclasspath_fragment { name: "myfragment", @@ -212,7 +212,7 @@ func TestBootclasspathFragment_StubLibs(t *testing.T) { api: { stub_libs: [ "mystublib", - "mysdklibrary", + "myothersdklibrary", ], }, core_platform_api: { @@ -231,36 +231,50 @@ func TestBootclasspathFragment_StubLibs(t *testing.T) { java_sdk_library { name: "mysdklibrary", srcs: ["a.java"], - compile_dex: true, + shared_library: false, public: {enabled: true}, system: {enabled: true}, } java_sdk_library { + name: "myothersdklibrary", + srcs: ["a.java"], + shared_library: false, + public: {enabled: true}, + } + + java_sdk_library { name: "mycoreplatform", srcs: ["a.java"], - compile_dex: true, + shared_library: false, public: {enabled: true}, } `) fragment := result.Module("myfragment", "android_common") - info := result.ModuleProvider(fragment, bootclasspathApiInfoProvider).(bootclasspathApiInfo) + info := result.ModuleProvider(fragment, HiddenAPIInfoProvider).(HiddenAPIInfo) stubsJar := "out/soong/.intermediates/mystublib/android_common/dex/mystublib.jar" - // Check that SdkPublic uses public stubs. + // Stubs jars for mysdklibrary publicStubsJar := "out/soong/.intermediates/mysdklibrary.stubs/android_common/dex/mysdklibrary.stubs.jar" - android.AssertPathsRelativeToTopEquals(t, "public dex stubs jar", []string{stubsJar, publicStubsJar}, info.stubJarsByKind[android.SdkPublic]) - - // Check that SdkSystem uses system stubs. systemStubsJar := "out/soong/.intermediates/mysdklibrary.stubs.system/android_common/dex/mysdklibrary.stubs.system.jar" - android.AssertPathsRelativeToTopEquals(t, "system dex stubs jar", []string{stubsJar, systemStubsJar}, info.stubJarsByKind[android.SdkSystem]) - // Check that SdkTest also uses system stubs as the mysdklibrary does not provide test stubs. - android.AssertPathsRelativeToTopEquals(t, "test dex stubs jar", []string{stubsJar, systemStubsJar}, info.stubJarsByKind[android.SdkTest]) + // Stubs jars for myothersdklibrary + otherPublicStubsJar := "out/soong/.intermediates/myothersdklibrary.stubs/android_common/dex/myothersdklibrary.stubs.jar" + + // Check that SdkPublic uses public stubs for all sdk libraries. + android.AssertPathsRelativeToTopEquals(t, "public dex stubs jar", []string{otherPublicStubsJar, publicStubsJar, stubsJar}, info.TransitiveStubDexJarsByKind[android.SdkPublic]) + + // Check that SdkSystem uses system stubs for mysdklibrary and public stubs for myothersdklibrary + // as it does not provide system stubs. + android.AssertPathsRelativeToTopEquals(t, "system dex stubs jar", []string{otherPublicStubsJar, systemStubsJar, stubsJar}, info.TransitiveStubDexJarsByKind[android.SdkSystem]) + + // Check that SdkTest also uses system stubs for mysdklibrary as it does not provide test stubs + // and public stubs for myothersdklibrary as it does not provide test stubs either. + android.AssertPathsRelativeToTopEquals(t, "test dex stubs jar", []string{otherPublicStubsJar, systemStubsJar, stubsJar}, info.TransitiveStubDexJarsByKind[android.SdkTest]) // Check that SdkCorePlatform uses public stubs from the mycoreplatform library. corePlatformStubsJar := "out/soong/.intermediates/mycoreplatform.stubs/android_common/dex/mycoreplatform.stubs.jar" - android.AssertPathsRelativeToTopEquals(t, "core platform dex stubs jar", []string{corePlatformStubsJar}, info.stubJarsByKind[android.SdkCorePlatform]) + android.AssertPathsRelativeToTopEquals(t, "core platform dex stubs jar", []string{corePlatformStubsJar}, info.TransitiveStubDexJarsByKind[android.SdkCorePlatform]) } diff --git a/java/classpath_fragment.go b/java/classpath_fragment.go index 7422fa2d5..bc0416a47 100644 --- a/java/classpath_fragment.go +++ b/java/classpath_fragment.go @@ -18,6 +18,7 @@ package java import ( "fmt" + "github.com/google/blueprint" "strings" "android/soong/android" @@ -120,6 +121,12 @@ func (c *ClasspathFragmentBase) generateClasspathProtoBuildActions(ctx android.M FlagWithOutput("--output=", c.outputFilepath) rule.Build("classpath_fragment", "Compiling "+c.outputFilepath.String()) + + classpathProtoInfo := ClasspathFragmentProtoContentInfo{ + ClasspathFragmentProtoInstallDir: c.installDirPath, + ClasspathFragmentProtoOutput: c.outputFilepath, + } + ctx.SetProvider(ClasspathFragmentProtoContentInfoProvider, classpathProtoInfo) } func writeClasspathsJson(ctx android.ModuleContext, output android.WritablePath, jars []classpathJar) { @@ -129,7 +136,7 @@ func writeClasspathsJson(ctx android.ModuleContext, output android.WritablePath, for idx, jar := range jars { fmt.Fprintf(&content, "{\n") - fmt.Fprintf(&content, "\"relativePath\": \"%s\",\n", jar.path) + fmt.Fprintf(&content, "\"path\": \"%s\",\n", jar.path) fmt.Fprintf(&content, "\"classpath\": \"%s\"\n", jar.classpath) if idx < len(jars)-1 { @@ -157,3 +164,23 @@ func (c *ClasspathFragmentBase) androidMkEntries() []android.AndroidMkEntries { }, }} } + +var ClasspathFragmentProtoContentInfoProvider = blueprint.NewProvider(ClasspathFragmentProtoContentInfo{}) + +type ClasspathFragmentProtoContentInfo struct { + // ClasspathFragmentProtoOutput is an output path for the generated classpaths.proto config of this module. + // + // The file should be copied to a relevant place on device, see ClasspathFragmentProtoInstallDir + // for more details. + ClasspathFragmentProtoOutput android.OutputPath + + // ClasspathFragmentProtoInstallDir contains information about on device location for the generated classpaths.proto file. + // + // The path encodes expected sub-location within partitions, i.e. etc/classpaths/<proto-file>, + // for ClasspathFragmentProtoOutput. To get sub-location, instead of the full output / make path + // use android.InstallPath#Rel(). + // + // This is only relevant for APEX modules as they perform their own installation; while regular + // system files are installed via ClasspathFragmentBase#androidMkEntries(). + ClasspathFragmentProtoInstallDir android.InstallPath +} diff --git a/java/dexpreopt.go b/java/dexpreopt.go index 8d23ad6bd..2e46d74fa 100644 --- a/java/dexpreopt.go +++ b/java/dexpreopt.go @@ -184,7 +184,7 @@ func (d *dexpreopter) dexpreopt(ctx android.ModuleContext, dexJarFile android.Wr imagesDeps = append(imagesDeps, variant.imagesDeps) } // The image locations for all Android variants are identical. - hostImageLocations := bootImage.getAnyAndroidVariant().imageLocations() + hostImageLocations, deviceImageLocations := bootImage.getAnyAndroidVariant().imageLocations() var profileClassListing android.OptionalPath var profileBootListing android.OptionalPath @@ -224,9 +224,10 @@ func (d *dexpreopter) dexpreopt(ctx android.ModuleContext, dexJarFile android.Wr ProvidesUsesLibrary: providesUsesLib, ClassLoaderContexts: d.classLoaderContexts, - Archs: archs, - DexPreoptImagesDeps: imagesDeps, - DexPreoptImageLocationsOnHost: hostImageLocations, + Archs: archs, + DexPreoptImagesDeps: imagesDeps, + DexPreoptImageLocationsOnHost: hostImageLocations, + DexPreoptImageLocationsOnDevice: deviceImageLocations, PreoptBootClassPathDexFiles: dexFiles.Paths(), PreoptBootClassPathDexLocations: dexLocations, diff --git a/java/dexpreopt_bootjars.go b/java/dexpreopt_bootjars.go index ce68c4856..e1a36507a 100644 --- a/java/dexpreopt_bootjars.go +++ b/java/dexpreopt_bootjars.go @@ -244,6 +244,9 @@ type bootImageConfig struct { // Subdirectory where the image files are installed. installDirOnHost string + // Subdirectory where the image files on device are installed. + installDirOnDevice string + // A list of (location, jar) pairs for the Java modules in this image. modules android.ConfiguredJarList @@ -273,8 +276,9 @@ type bootImageVariant struct { dexLocationsDeps []string // for the dependency images and in this image // Paths to image files. - imagePathOnHost android.OutputPath // first image file - imagesDeps android.OutputPaths // all files + imagePathOnHost android.OutputPath // first image file path on host + imagePathOnDevice string // first image file path on device + imagesDeps android.OutputPaths // all files // Only for extensions, paths to the primary boot images. primaryImages android.OutputPath @@ -361,11 +365,12 @@ func (image bootImageConfig) moduleFiles(ctx android.PathContext, dir android.Ou // The location is passed as an argument to the ART tools like dex2oat instead of the real path. // ART tools will then reconstruct the architecture-specific real path. // -func (image *bootImageVariant) imageLocations() (imageLocations []string) { +func (image *bootImageVariant) imageLocations() (imageLocationsOnHost []string, imageLocationsOnDevice []string) { if image.extends != nil { - imageLocations = image.extends.getVariant(image.target).imageLocations() + imageLocationsOnHost, imageLocationsOnDevice = image.extends.getVariant(image.target).imageLocations() } - return append(imageLocations, dexpreopt.PathToLocation(image.imagePathOnHost, image.target.Arch.ArchType)) + return append(imageLocationsOnHost, dexpreopt.PathToLocation(image.imagePathOnHost, image.target.Arch.ArchType)), + append(imageLocationsOnDevice, dexpreopt.PathStringToLocation(image.imagePathOnDevice, image.target.Arch.ArchType)) } func dexpreoptBootJarsFactory() android.SingletonModule { @@ -427,17 +432,10 @@ func (d *dexpreoptBootJars) GenerateSingletonBuildActions(ctx android.SingletonC return } - // Generate the profile rule from the default boot image. defaultImageConfig := defaultBootImageConfig(ctx) - profile := bootImageProfileRule(ctx, defaultImageConfig) - - // Create the default boot image. - d.defaultBootImage = buildBootImage(ctx, defaultImageConfig, profile) - - // Create boot image for the ART apex (build artifacts are accessed via the global boot image config). - d.otherImages = append(d.otherImages, buildBootImage(ctx, artBootImageConfig(ctx), profile)) - - copyUpdatableBootJars(ctx) + d.defaultBootImage = defaultImageConfig + artBootImageConfig := artBootImageConfig(ctx) + d.otherImages = []*bootImageConfig{artBootImageConfig} } // shouldBuildBootImages determines whether boot images should be built. @@ -452,77 +450,19 @@ func shouldBuildBootImages(config android.Config, global *dexpreopt.GlobalConfig return true } -// A copy of isModuleInConfiguredList created to work with singleton context. -// -// TODO(b/177892522): Remove this. -func isModuleInConfiguredListForSingleton(ctx android.SingletonContext, module android.Module, configuredBootJars android.ConfiguredJarList) bool { - name := ctx.ModuleName(module) - - // Strip a prebuilt_ prefix so that this can match a prebuilt module that has not been renamed. - name = android.RemoveOptionalPrebuiltPrefix(name) - - // Ignore any module that is not listed in the boot image configuration. - index := configuredBootJars.IndexOfJar(name) - if index == -1 { - return false - } - - // It is an error if the module is not an ApexModule. - if _, ok := module.(android.ApexModule); !ok { - ctx.Errorf("%s is configured in boot jars but does not support being added to an apex", ctx.ModuleName(module)) - return false - } - - apexInfo := ctx.ModuleProvider(module, android.ApexInfoProvider).(android.ApexInfo) - - // Now match the apex part of the boot image configuration. - requiredApex := configuredBootJars.Apex(index) - if requiredApex == "platform" || requiredApex == "system_ext" { - if len(apexInfo.InApexes) != 0 { - // A platform variant is required but this is for an apex so ignore it. - return false - } - } else if !apexInfo.InApexByBaseName(requiredApex) { - // An apex variant for a specific apex is required but this is the wrong apex. - return false - } - - return true -} - -// findBootJarModules finds the boot jar module variants specified in the bootjars parameter. -// -// It returns a list of modules such that the module at index i corresponds to the configured jar -// at index i. -func findBootJarModules(ctx android.SingletonContext, bootjars android.ConfiguredJarList) []android.Module { - modules := make([]android.Module, bootjars.Len()) - - // This logic is tested in the apex package to avoid import cycle apex <-> java. - ctx.VisitAllModules(func(module android.Module) { - if !isActiveModule(module) || !isModuleInConfiguredListForSingleton(ctx, module, bootjars) { - return - } - - name := android.RemoveOptionalPrebuiltPrefix(ctx.ModuleName(module)) - index := bootjars.IndexOfJar(name) - if existing := modules[index]; existing != nil { - ctx.Errorf("Multiple boot jar modules found for %s:%s - %q and %q", - bootjars.Apex(index), bootjars.Jar(index), existing, module) - return - } - modules[index] = module - }) - return modules -} - // copyBootJarsToPredefinedLocations generates commands that will copy boot jars to // predefined paths in the global config. -func copyBootJarsToPredefinedLocations(ctx android.SingletonContext, bootModules []android.Module, bootjars android.ConfiguredJarList, jarPathsPredefined android.WritablePaths) { +func copyBootJarsToPredefinedLocations(ctx android.ModuleContext, bootModules []android.Module, bootjars android.ConfiguredJarList, jarPathsPredefined android.WritablePaths) { jarPaths := make(android.Paths, bootjars.Len()) for i, module := range bootModules { if module != nil { bootDexJar := module.(interface{ DexJarBuildPath() android.Path }).DexJarBuildPath() jarPaths[i] = bootDexJar + + name := android.RemoveOptionalPrebuiltPrefix(ctx.OtherModuleName(module)) + if bootjars.Jar(i) != name { + ctx.ModuleErrorf("expected module %s at position %d but found %s", bootjars.Jar(i), i, name) + } } } @@ -548,7 +488,7 @@ func copyBootJarsToPredefinedLocations(ctx android.SingletonContext, bootModules }, }) } else { - ctx.Errorf("failed to find a dex jar path for module '%s'"+ + ctx.ModuleErrorf("failed to find a dex jar path for module '%s'"+ ", note that some jars may be filtered out by module constraints", module) } @@ -563,10 +503,7 @@ func copyBootJarsToPredefinedLocations(ctx android.SingletonContext, bootModules } // buildBootImage takes a bootImageConfig, creates rules to build it, and returns the image. -func buildBootImage(ctx android.SingletonContext, image *bootImageConfig, profile android.WritablePath) *bootImageConfig { - bootModules := findBootJarModules(ctx, image.modules) - copyBootJarsToPredefinedLocations(ctx, bootModules, image.modules, image.dexPaths) - +func buildBootImage(ctx android.ModuleContext, image *bootImageConfig, profile android.WritablePath) { var zipFiles android.Paths for _, variant := range image.variants { files := buildBootImageVariant(ctx, variant, profile) @@ -585,19 +522,10 @@ func buildBootImage(ctx android.SingletonContext, image *bootImageConfig, profil rule.Build("zip_"+image.name, "zip "+image.name+" image") } - - return image -} - -// Generate commands that will copy updatable boot jars to predefined paths in the global config. -func copyUpdatableBootJars(ctx android.SingletonContext) { - config := GetUpdatableBootConfig(ctx) - bootModules := findBootJarModules(ctx, config.modules) - copyBootJarsToPredefinedLocations(ctx, bootModules, config.modules, config.dexPaths) } // Generate boot image build rules for a specific target. -func buildBootImageVariant(ctx android.SingletonContext, image *bootImageVariant, profile android.Path) android.WritablePaths { +func buildBootImageVariant(ctx android.ModuleContext, image *bootImageVariant, profile android.Path) android.WritablePaths { globalSoong := dexpreopt.GetCachedGlobalSoongConfig(ctx) global := dexpreopt.GetGlobalConfig(ctx) @@ -747,7 +675,7 @@ const failureMessage = `ERROR: Dex2oat failed to compile a boot image. 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 *bootImageConfig) android.WritablePath { +func bootImageProfileRule(ctx android.ModuleContext, image *bootImageConfig) android.WritablePath { globalSoong := dexpreopt.GetCachedGlobalSoongConfig(ctx) global := dexpreopt.GetGlobalConfig(ctx) @@ -838,7 +766,7 @@ func generateUpdatableBcpPackagesRule(ctx android.ModuleContext, image *bootImag if len(pp) > 0 { updatablePackages = append(updatablePackages, pp...) } else { - ctx.ModuleErrorf("Missing permitted_packages") + ctx.OtherModuleErrorf(module, "Missing permitted_packages") } } } @@ -873,12 +801,12 @@ func dumpOatRules(ctx android.ModuleContext, image *bootImageConfig) { // Create a rule to call oatdump. output := android.PathForOutput(ctx, "boot."+suffix+".oatdump.txt") rule := android.NewRuleBuilder(pctx, ctx) + imageLocationsOnHost, _ := image.imageLocations() rule.Command(). - // TODO: for now, use the debug version for better error reporting - BuiltTool("oatdumpd"). + BuiltTool("oatdump"). FlagWithInputList("--runtime-arg -Xbootclasspath:", image.dexPathsDeps.Paths(), ":"). FlagWithList("--runtime-arg -Xbootclasspath-locations:", image.dexLocationsDeps, ":"). - FlagWithArg("--image=", strings.Join(image.imageLocations(), ":")).Implicits(image.imagesDeps.Paths()). + FlagWithArg("--image=", strings.Join(imageLocationsOnHost, ":")).Implicits(image.imagesDeps.Paths()). FlagWithOutput("--output=", output). FlagWithArg("--instruction-set=", arch.String()) rule.Build("dump-oat-boot-"+suffix, "dump oat boot "+arch.String()) @@ -948,8 +876,8 @@ func (d *dexpreoptBootJars) MakeVars(ctx android.MakeVarsContext) { ctx.Strict("DEXPREOPT_IMAGE_BUILT_INSTALLED_"+sfx, variant.installs.String()) ctx.Strict("DEXPREOPT_IMAGE_UNSTRIPPED_BUILT_INSTALLED_"+sfx, variant.unstrippedInstalls.String()) } - imageLocations := current.getAnyAndroidVariant().imageLocations() - ctx.Strict("DEXPREOPT_IMAGE_LOCATIONS_"+current.name, strings.Join(imageLocations, ":")) + imageLocationsOnHost, _ := current.getAnyAndroidVariant().imageLocations() + ctx.Strict("DEXPREOPT_IMAGE_LOCATIONS_"+current.name, strings.Join(imageLocationsOnHost, ":")) ctx.Strict("DEXPREOPT_IMAGE_ZIP_"+current.name, current.zip.String()) } ctx.Strict("DEXPREOPT_IMAGE_NAMES", strings.Join(imageNames, " ")) diff --git a/java/dexpreopt_bootjars_test.go b/java/dexpreopt_bootjars_test.go index 73f21d12e..bc7a55eb9 100644 --- a/java/dexpreopt_bootjars_test.go +++ b/java/dexpreopt_bootjars_test.go @@ -20,7 +20,6 @@ import ( "testing" "android/soong/android" - "android/soong/dexpreopt" ) func testDexpreoptBoot(t *testing.T, ruleFile string, expectedInputs, expectedOutputs []string) { @@ -42,17 +41,21 @@ func testDexpreoptBoot(t *testing.T, ruleFile string, expectedInputs, expectedOu name: "baz", jars: ["a.jar"], } + + platform_bootclasspath { + name: "platform-bootclasspath", + } ` result := android.GroupFixturePreparers( prepareForJavaTest, PrepareForTestWithJavaSdkLibraryFiles, FixtureWithLastReleaseApis("foo"), - dexpreopt.FixtureSetBootJars("platform:foo", "system_ext:bar", "platform:baz"), + FixtureConfigureBootJars("platform:foo", "system_ext:bar", "platform:baz"), ).RunTestWithBp(t, bp) - dexpreoptBootJars := result.SingletonForTests("dex_bootjars") - rule := dexpreoptBootJars.Output(ruleFile) + platformBootclasspath := result.ModuleForTests("platform-bootclasspath", "android_common") + rule := platformBootclasspath.Output(ruleFile) for i := range expectedInputs { expectedInputs[i] = filepath.Join("out/soong/test_device", expectedInputs[i]) diff --git a/java/dexpreopt_config.go b/java/dexpreopt_config.go index 7fb0444c2..39a3e11a5 100644 --- a/java/dexpreopt_config.go +++ b/java/dexpreopt_config.go @@ -15,7 +15,6 @@ package java import ( - "fmt" "path/filepath" "strings" @@ -23,32 +22,6 @@ import ( "android/soong/dexpreopt" ) -// 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 { - return ctx.Config().OnceStringSlice(systemServerClasspathKey, func() []string { - global := dexpreopt.GetGlobalConfig(ctx) - var systemServerClasspathLocations []string - nonUpdatable := dexpreopt.NonUpdatableSystemServerJars(ctx, global) - // 1) Non-updatable jars. - for _, m := range nonUpdatable { - systemServerClasspathLocations = append(systemServerClasspathLocations, - filepath.Join("/system/framework", m+".jar")) - } - // 2) The jars that are from an updatable apex. - systemServerClasspathLocations = append(systemServerClasspathLocations, - global.UpdatableSystemServerJars.DevicePaths(ctx.Config(), android.Android)...) - - if expectedLen := global.SystemServerJars.Len() + global.UpdatableSystemServerJars.Len(); expectedLen != len(systemServerClasspathLocations) { - panic(fmt.Errorf("wrong number of system server jars, got %d, expected %d", len(systemServerClasspathLocations), expectedLen)) - } - return systemServerClasspathLocations - }) -} - -var systemServerClasspathKey = android.NewOnceKey("systemServerClasspath") - // dexpreoptTargets returns the list of targets that are relevant to dexpreopting, which excludes architectures // supported through native bridge. func dexpreoptTargets(ctx android.PathContext) []android.Target { @@ -84,25 +57,28 @@ func genBootImageConfigs(ctx android.PathContext) map[string]*bootImageConfig { frameworkModules := global.BootJars.RemoveList(artModules) artDirOnHost := "apex/art_boot_images/javalib" + artDirOnDevice := "apex/com.android.art/javalib" frameworkSubdir := "system/framework" // ART config for the primary boot image in the ART apex. // It includes the Core Libraries. artCfg := bootImageConfig{ - name: artBootImageName, - stem: "boot", - installDirOnHost: artDirOnHost, - modules: artModules, + name: artBootImageName, + stem: "boot", + installDirOnHost: artDirOnHost, + installDirOnDevice: artDirOnDevice, + modules: artModules, } // Framework config for the boot image extension. // It includes framework libraries and depends on the ART config. frameworkCfg := bootImageConfig{ - extends: &artCfg, - name: frameworkBootImageName, - stem: "boot", - installDirOnHost: frameworkSubdir, - modules: frameworkModules, + extends: &artCfg, + name: frameworkBootImageName, + stem: "boot", + installDirOnHost: frameworkSubdir, + installDirOnDevice: frameworkSubdir, + modules: frameworkModules, } configs := map[string]*bootImageConfig{ @@ -131,11 +107,12 @@ func genBootImageConfigs(ctx android.PathContext) map[string]*bootImageConfig { arch := target.Arch.ArchType imageDir := c.dir.Join(ctx, target.Os.String(), c.installDirOnHost, arch.String()) variant := &bootImageVariant{ - bootImageConfig: c, - target: target, - imagePathOnHost: imageDir.Join(ctx, imageName), - imagesDeps: c.moduleFiles(ctx, imageDir, ".art", ".oat", ".vdex"), - dexLocations: c.modules.DevicePaths(ctx.Config(), target.Os), + bootImageConfig: c, + target: target, + imagePathOnHost: imageDir.Join(ctx, imageName), + imagePathOnDevice: filepath.Join("/", c.installDirOnDevice, arch.String(), imageName), + imagesDeps: c.moduleFiles(ctx, imageDir, ".art", ".oat", ".vdex"), + dexLocations: c.modules.DevicePaths(ctx.Config(), target.Os), } variant.dexLocationsDeps = variant.dexLocations c.variants = append(c.variants, variant) diff --git a/java/dexpreopt_test.go b/java/dexpreopt_test.go index a9e0773b7..b25deceac 100644 --- a/java/dexpreopt_test.go +++ b/java/dexpreopt_test.go @@ -15,7 +15,12 @@ package java import ( + "fmt" "testing" + + "android/soong/android" + "android/soong/cc" + "android/soong/dexpreopt" ) func TestDexpreoptEnabled(t *testing.T) { @@ -166,3 +171,51 @@ func enabledString(enabled bool) string { return "disabled" } } + +func TestDex2oatToolDeps(t *testing.T) { + if android.BuildOs != android.Linux { + // The host binary paths checked below are build OS dependent. + t.Skipf("Unsupported build OS %s", android.BuildOs) + } + + preparers := android.GroupFixturePreparers( + cc.PrepareForTestWithCcDefaultModules, + PrepareForTestWithJavaDefaultModulesWithoutFakeDex2oatd, + dexpreopt.PrepareForTestByEnablingDexpreopt) + + testDex2oatToolDep := func(sourceEnabled, prebuiltEnabled, prebuiltPreferred bool, + expectedDex2oatPath string) { + name := fmt.Sprintf("sourceEnabled:%t,prebuiltEnabled:%t,prebuiltPreferred:%t", + sourceEnabled, prebuiltEnabled, prebuiltPreferred) + t.Run(name, func(t *testing.T) { + result := preparers.RunTestWithBp(t, fmt.Sprintf(` + cc_binary { + name: "dex2oatd", + enabled: %t, + host_supported: true, + } + cc_prebuilt_binary { + name: "dex2oatd", + enabled: %t, + prefer: %t, + host_supported: true, + srcs: ["x86_64/bin/dex2oatd"], + } + java_library { + name: "myjavalib", + } + `, sourceEnabled, prebuiltEnabled, prebuiltPreferred)) + pathContext := android.PathContextForTesting(result.Config) + dex2oatPath := dexpreopt.GetCachedGlobalSoongConfig(pathContext).Dex2oat + android.AssertStringEquals(t, "Testing "+name, expectedDex2oatPath, android.NormalizePathForTesting(dex2oatPath)) + }) + } + + sourceDex2oatPath := "host/linux-x86/bin/dex2oatd" + prebuiltDex2oatPath := ".intermediates/prebuilt_dex2oatd/linux_glibc_x86_64/dex2oatd" + + testDex2oatToolDep(true, false, false, sourceDex2oatPath) + testDex2oatToolDep(true, true, false, sourceDex2oatPath) + testDex2oatToolDep(true, true, true, prebuiltDex2oatPath) + testDex2oatToolDep(false, true, false, prebuiltDex2oatPath) +} diff --git a/java/hiddenapi.go b/java/hiddenapi.go index a34044f32..e9693c68e 100644 --- a/java/hiddenapi.go +++ b/java/hiddenapi.go @@ -26,27 +26,10 @@ var hiddenAPIGenerateCSVRule = pctx.AndroidStaticRule("hiddenAPIGenerateCSV", bl }, "outFlag", "stubAPIFlags") type hiddenAPI struct { - // The name of the module as it would be used in the boot jars configuration, e.g. without any - // prebuilt_ prefix (if it is a prebuilt) and without any ".impl" suffix if it is a - // java_sdk_library implementation library. - configurationName string - // True if the module containing this structure contributes to the hiddenapi information or has // that information encoded within it. active bool - // Identifies the active module variant which will be used as the source of hiddenapi information. - // - // A class may be compiled into a number of different module variants each of which will need the - // hiddenapi information encoded into it and so will be marked as active. However, only one of - // them must be used as a source of information by hiddenapi otherwise it will end up with - // duplicate entries. That module will have primary=true. - // - // Note, that modules <x>-hiddenapi that provide additional annotation information for module <x> - // that is on the bootclasspath are marked as primary=true as they are the primary source of that - // annotation information. - primary bool - // The path to the dex jar that is in the boot class path. If this is nil then the associated // module is not a boot jar, but could be one of the <x>-hiddenapi modules that provide additional // annotations for the <x> boot dex jar but which do not actually provide a boot dex jar @@ -56,130 +39,82 @@ type hiddenAPI struct { // this file so using the encoded dex jar here would result in a cycle in the ninja rules. bootDexJarPath android.Path - // The path to the CSV file that contains mappings from Java signature to various flags derived - // from annotations in the source, e.g. whether it is public or the sdk version above which it - // can no longer be used. - // - // It is created by the Class2NonSdkList tool which processes the .class files in the class - // implementation jar looking for UnsupportedAppUsage and CovariantReturnType annotations. The - // tool also consumes the hiddenAPISingletonPathsStruct.stubFlags file in order to perform - // consistency checks on the information in the annotations and to filter out bridge methods - // that are already part of the public API. - flagsCSVPath android.Path - - // The path to the CSV file that contains mappings from Java signature to the value of properties - // specified on UnsupportedAppUsage annotations in the source. - // - // Like the flagsCSVPath file this is also created by the Class2NonSdkList in the same way. - // Although the two files could potentially be created in a single invocation of the - // Class2NonSdkList at the moment they are created using their own invocation, with the behavior - // being determined by the property that is used. - metadataCSVPath android.Path - - // The path to the CSV file that contains mappings from Java signature to source location - // information. - // - // It is created by the merge_csv tool which processes the class implementation jar, extracting - // all the files ending in .uau (which are CSV files) and merges them together. The .uau files are - // created by the unsupported app usage annotation processor during compilation of the class - // implementation jar. - indexCSVPath android.Path - // The paths to the classes jars that contain classes and class members annotated with // the UnsupportedAppUsage annotation that need to be extracted as part of the hidden API // processing. classesJarPaths android.Paths -} - -func (h *hiddenAPI) flagsCSV() android.Path { - return h.flagsCSVPath -} -func (h *hiddenAPI) metadataCSV() android.Path { - return h.metadataCSVPath + // The compressed state of the dex file being encoded. This is used to ensure that the encoded + // dex file has the same state. + uncompressDexState *bool } func (h *hiddenAPI) bootDexJar() android.Path { return h.bootDexJarPath } -func (h *hiddenAPI) indexCSV() android.Path { - return h.indexCSVPath -} - func (h *hiddenAPI) classesJars() android.Paths { return h.classesJarPaths } +func (h *hiddenAPI) uncompressDex() *bool { + return h.uncompressDexState +} + +// hiddenAPIModule is the interface a module that embeds the hiddenAPI structure must implement. +type hiddenAPIModule interface { + android.Module + hiddenAPIIntf +} + type hiddenAPIIntf interface { bootDexJar() android.Path - flagsCSV() android.Path - indexCSV() android.Path - metadataCSV() android.Path classesJars() android.Paths + uncompressDex() *bool } var _ hiddenAPIIntf = (*hiddenAPI)(nil) // Initialize the hiddenapi structure -func (h *hiddenAPI) initHiddenAPI(ctx android.BaseModuleContext, configurationName string) { +// +// uncompressedDexState should be nil when the module is a prebuilt and so does not require hidden +// API encoding. +func (h *hiddenAPI) initHiddenAPI(ctx android.ModuleContext, dexJar, classesJar android.Path, uncompressedDexState *bool) { + + // Save the classes jars even if this is not active as they may be used by modular hidden API + // processing. + classesJars := android.Paths{classesJar} + ctx.VisitDirectDepsWithTag(hiddenApiAnnotationsTag, func(dep android.Module) { + javaInfo := ctx.OtherModuleProvider(dep, JavaInfoProvider).(JavaInfo) + classesJars = append(classesJars, javaInfo.ImplementationJars...) + }) + h.classesJarPaths = classesJars + + // Save the unencoded dex jar so it can be used when generating the + // hiddenAPISingletonPathsStruct.stubFlags file. + h.bootDexJarPath = dexJar + + h.uncompressDexState = uncompressedDexState + // If hiddenapi processing is disabled treat this as inactive. if ctx.Config().IsEnvTrue("UNSAFE_DISABLE_HIDDENAPI_FLAGS") { return } - h.configurationName = configurationName + // The context module must implement hiddenAPIModule. + module := ctx.Module().(hiddenAPIModule) + + // If the frameworks/base directories does not exist and no prebuilt hidden API flag files have + // been configured then it is not possible to do hidden API encoding. + if !ctx.Config().FrameworksBaseDirExists(ctx) && ctx.Config().PrebuiltHiddenApiDir(ctx) == "" { + return + } // It is important that hiddenapi information is only gathered for/from modules that are actually // on the boot jars list because the runtime only enforces access to the hidden API for the // bootclassloader. If information is gathered for modules not on the list then that will cause // failures in the CtsHiddenApiBlocklist... tests. - module := ctx.Module() h.active = isModuleInBootClassPath(ctx, module) - if !h.active { - // The rest of the properties will be ignored if active is false. - return - } - - // Determine whether this module is the primary module or not. - primary := true - - // A prebuilt module is only primary if it is preferred and conversely a source module is only - // primary if it has not been replaced by a prebuilt module. - if pi, ok := module.(android.PrebuiltInterface); ok { - if p := pi.Prebuilt(); p != nil { - primary = p.UsePrebuilt() - } - } else { - // The only module that will pass a different configurationName to its module name to this - // method is the implementation library of a java_sdk_library. It has a configuration name of - // <x> the same as its parent java_sdk_library but a module name of <x>.impl. It is not the - // primary module, the java_sdk_library with the name of <x> is. - primary = configurationName == ctx.ModuleName() - - // A source module that has been replaced by a prebuilt can never be the primary module. - if module.IsReplacedByPrebuilt() { - if ctx.HasProvider(android.ApexInfoProvider) { - // The source module is in an APEX but the prebuilt module on which it depends is not in an - // APEX and so is not the one that will actually be used for hidden API processing. That - // means it is not possible to check to see if it is a suitable replacement so just assume - // that it is. - primary = false - } else { - ctx.VisitDirectDepsWithTag(android.PrebuiltDepTag, func(prebuilt android.Module) { - if h, ok := prebuilt.(hiddenAPIIntf); ok && h.bootDexJar() != nil { - primary = false - } else { - ctx.ModuleErrorf( - "hiddenapi has determined that the source module %q should be ignored as it has been"+ - " replaced by the prebuilt module %q but unfortunately it does not provide a"+ - " suitable boot dex jar", ctx.ModuleName(), ctx.OtherModuleName(prebuilt)) - } - }) - } - } - } - h.primary = primary } func isModuleInBootClassPath(ctx android.BaseModuleContext, module android.Module) bool { @@ -191,84 +126,74 @@ func isModuleInBootClassPath(ctx android.BaseModuleContext, module android.Modul return active } -// hiddenAPIExtractAndEncode is called by any module that could contribute to the hiddenapi -// processing. +// hiddenAPIEncodeDex is called by any module that needs to encode dex files. // // It ignores any module that has not had initHiddenApi() called on it and which is not in the boot -// jar list. +// jar list. In that case it simply returns the supplied dex jar path. // -// Otherwise, it generates ninja rules to do the following: -// 1. Extract information needed for hiddenapi processing from the module and output it into CSV -// files. -// 2. Conditionally adds the supplied dex file to the list of files used to generate the -// hiddenAPISingletonPathsStruct.stubsFlag file. -// 3. Conditionally creates a copy of the supplied dex file into which it has encoded the hiddenapi -// flags and returns this instead of the supplied dex jar, otherwise simply returns the supplied -// dex jar. -func (h *hiddenAPI) hiddenAPIExtractAndEncode(ctx android.ModuleContext, dexJar android.OutputPath, - implementationJar android.Path, uncompressDex bool) android.OutputPath { +// Otherwise, it creates a copy of the supplied dex file into which it has encoded the hiddenapi +// flags and returns this instead of the supplied dex jar. +func (h *hiddenAPI) hiddenAPIEncodeDex(ctx android.ModuleContext, dexJar android.OutputPath) android.OutputPath { if !h.active { return dexJar } - h.hiddenAPIExtractInformation(ctx, dexJar, implementationJar) - - hiddenAPIJar := android.PathForModuleOut(ctx, "hiddenapi", h.configurationName+".jar").OutputPath + // A nil uncompressDexState prevents the dex file from being encoded. + if h.uncompressDexState == nil { + ctx.ModuleErrorf("cannot encode dex file %s when uncompressDexState is nil", dexJar) + } + uncompressDex := *h.uncompressDexState // Create a copy of the dex jar which has been encoded with hiddenapi flags. - hiddenAPIEncodeDex(ctx, hiddenAPIJar, dexJar, uncompressDex) + flagsCSV := hiddenAPISingletonPaths(ctx).flags + outputDir := android.PathForModuleOut(ctx, "hiddenapi").OutputPath + encodedDex := hiddenAPIEncodeDex(ctx, dexJar, flagsCSV, uncompressDex, outputDir) // Use the encoded dex jar from here onwards. - dexJar = hiddenAPIJar - - return dexJar + return encodedDex } -// hiddenAPIExtractInformation generates ninja rules to extract the information from the classes -// jar, and outputs it to the appropriate module specific CSV file. +// buildRuleToGenerateAnnotationFlags builds a ninja rule to generate the annotation-flags.csv file +// from the classes jars and stub-flags.csv files. // -// It also makes the dex jar available for use when generating the -// hiddenAPISingletonPathsStruct.stubFlags. -func (h *hiddenAPI) hiddenAPIExtractInformation(ctx android.ModuleContext, dexJar, classesJar android.Path) { - if !h.active { - return - } - - // More than one library with the same classes may need to be encoded but only one should be - // used as a source of information for hidden API processing otherwise it will result in - // duplicate entries in the files. - if !h.primary { - return - } - - classesJars := android.Paths{classesJar} - ctx.VisitDirectDepsWithTag(hiddenApiAnnotationsTag, func(dep android.Module) { - javaInfo := ctx.OtherModuleProvider(dep, JavaInfoProvider).(JavaInfo) - classesJars = append(classesJars, javaInfo.ImplementationJars...) - }) - h.classesJarPaths = classesJars - - stubFlagsCSV := hiddenAPISingletonPaths(ctx).stubFlags - - flagsCSV := android.PathForModuleOut(ctx, "hiddenapi", "flags.csv") +// The annotation-flags.csv file contains mappings from Java signature to various flags derived from +// annotations in the source, e.g. whether it is public or the sdk version above which it can no +// longer be used. +// +// It is created by the Class2NonSdkList tool which processes the .class files in the class +// implementation jar looking for UnsupportedAppUsage and CovariantReturnType annotations. The +// tool also consumes the hiddenAPISingletonPathsStruct.stubFlags file in order to perform +// consistency checks on the information in the annotations and to filter out bridge methods +// that are already part of the public API. +func buildRuleToGenerateAnnotationFlags(ctx android.ModuleContext, desc string, classesJars android.Paths, stubFlagsCSV android.Path, outputPath android.WritablePath) { ctx.Build(pctx, android.BuildParams{ Rule: hiddenAPIGenerateCSVRule, - Description: "hiddenapi flags", + Description: desc, Inputs: classesJars, - Output: flagsCSV, + Output: outputPath, Implicit: stubFlagsCSV, Args: map[string]string{ "outFlag": "--write-flags-csv", "stubAPIFlags": stubFlagsCSV.String(), }, }) - h.flagsCSVPath = flagsCSV +} - metadataCSV := android.PathForModuleOut(ctx, "hiddenapi", "metadata.csv") +// buildRuleToGenerateMetadata builds a ninja rule to generate the metadata.csv file from +// the classes jars and stub-flags.csv files. +// +// The metadata.csv file contains mappings from Java signature to the value of properties specified +// on UnsupportedAppUsage annotations in the source. +// +// Like the annotation-flags.csv file this is also created by the Class2NonSdkList in the same way. +// Although the two files could potentially be created in a single invocation of the +// Class2NonSdkList at the moment they are created using their own invocation, with the behavior +// being determined by the property that is used. +func buildRuleToGenerateMetadata(ctx android.ModuleContext, desc string, classesJars android.Paths, stubFlagsCSV android.Path, metadataCSV android.WritablePath) { ctx.Build(pctx, android.BuildParams{ Rule: hiddenAPIGenerateCSVRule, - Description: "hiddenapi metadata", + Description: desc, Inputs: classesJars, Output: metadataCSV, Implicit: stubFlagsCSV, @@ -277,22 +202,27 @@ func (h *hiddenAPI) hiddenAPIExtractInformation(ctx android.ModuleContext, dexJa "stubAPIFlags": stubFlagsCSV.String(), }, }) - h.metadataCSVPath = metadataCSV +} - indexCSV := android.PathForModuleOut(ctx, "hiddenapi", "index.csv") +// buildRuleToGenerateIndex builds a ninja rule to generate the index.csv file from the classes +// jars. +// +// The index.csv file contains mappings from Java signature to source location information. +// +// It is created by the merge_csv tool which processes the class implementation jar, extracting +// all the files ending in .uau (which are CSV files) and merges them together. The .uau files are +// created by the unsupported app usage annotation processor during compilation of the class +// implementation jar. +func buildRuleToGenerateIndex(ctx android.ModuleContext, desc string, classesJars android.Paths, indexCSV android.WritablePath) { rule := android.NewRuleBuilder(pctx, ctx) rule.Command(). BuiltTool("merge_csv"). Flag("--zip_input"). Flag("--key_field signature"). + FlagWithArg("--header=", "signature,file,startline,startcol,endline,endcol,properties"). FlagWithOutput("--output=", indexCSV). Inputs(classesJars) - rule.Build("merged-hiddenapi-index", "Merged Hidden API index") - h.indexCSVPath = indexCSV - - // Save the unencoded dex jar so it can be used when generating the - // hiddenAPISingletonPathsStruct.stubFlags file. - h.bootDexJarPath = dexJar + rule.Build(desc, desc) } var hiddenAPIEncodeDexRule = pctx.AndroidStaticRule("hiddenAPIEncodeDex", blueprint.RuleParams{ @@ -303,7 +233,7 @@ var hiddenAPIEncodeDexRule = pctx.AndroidStaticRule("hiddenAPIEncodeDex", bluepr echo "--output-dex=$tmpDir/dex-output/$$(basename $${INPUT_DEX})"; done | xargs ${config.HiddenAPI} encode --api-flags=$flagsCsv $hiddenapiFlags && ${config.SoongZipCmd} $soongZipFlags -o $tmpDir/dex.jar -C $tmpDir/dex-output -f "$tmpDir/dex-output/classes*.dex" && - ${config.MergeZipsCmd} -D -zipToNotStrip $tmpDir/dex.jar -stripFile "classes*.dex" -stripFile "**/*.uau" $out $tmpDir/dex.jar $in`, + ${config.MergeZipsCmd} -j -D -zipToNotStrip $tmpDir/dex.jar -stripFile "classes*.dex" -stripFile "**/*.uau" $out $tmpDir/dex.jar $in`, CommandDeps: []string{ "${config.HiddenAPI}", "${config.SoongZipCmd}", @@ -311,39 +241,37 @@ var hiddenAPIEncodeDexRule = pctx.AndroidStaticRule("hiddenAPIEncodeDex", bluepr }, }, "flagsCsv", "hiddenapiFlags", "tmpDir", "soongZipFlags") -func hiddenAPIEncodeDex(ctx android.ModuleContext, output android.WritablePath, dexInput android.Path, - uncompressDex bool) { +// hiddenAPIEncodeDex generates the build rule that will encode the supplied dex jar and place the +// encoded dex jar in a file of the same name in the output directory. +// +// The encode dex rule requires unzipping, encoding and rezipping the classes.dex files along with +// all the resources from the input jar. It also ensures that if it was uncompressed in the input +// it stays uncompressed in the output. +func hiddenAPIEncodeDex(ctx android.ModuleContext, dexInput, flagsCSV android.Path, uncompressDex bool, outputDir android.OutputPath) android.OutputPath { - flagsCSV := hiddenAPISingletonPaths(ctx).flags + // The output file has the same name as the input file and is in the output directory. + output := outputDir.Join(ctx, dexInput.Base()) + + // Create a jar specific temporary directory in which to do the work just in case this is called + // with the same output directory for multiple modules. + tmpDir := outputDir.Join(ctx, dexInput.Base()+"-tmp") - // The encode dex rule requires unzipping and rezipping the classes.dex files, ensure that if it was uncompressed - // in the input it stays uncompressed in the output. + // If the input is uncompressed then generate the output of the encode rule to an intermediate + // file as the final output will need further processing after encoding. soongZipFlags := "" - hiddenapiFlags := "" - tmpOutput := output - tmpDir := android.PathForModuleOut(ctx, "hiddenapi", "dex") + encodeRuleOutput := output if uncompressDex { soongZipFlags = "-L 0" - tmpOutput = android.PathForModuleOut(ctx, "hiddenapi", "unaligned", "unaligned.jar") - tmpDir = android.PathForModuleOut(ctx, "hiddenapi", "unaligned") + encodeRuleOutput = outputDir.Join(ctx, "unaligned", dexInput.Base()) } - enforceHiddenApiFlagsToAllMembers := true - // If frameworks/base doesn't exist we must be building with the 'master-art' manifest. - // Disable assertion that all methods/fields have hidden API flags assigned. - if !ctx.Config().FrameworksBaseDirExists(ctx) { - enforceHiddenApiFlagsToAllMembers = false - } // b/149353192: when a module is instrumented, jacoco adds synthetic members // $jacocoData and $jacocoInit. Since they don't exist when building the hidden API flags, // don't complain when we don't find hidden API flags for the synthetic members. + hiddenapiFlags := "" if j, ok := ctx.Module().(interface { shouldInstrument(android.BaseModuleContext) bool }); ok && j.shouldInstrument(ctx) { - enforceHiddenApiFlagsToAllMembers = false - } - - if !enforceHiddenApiFlagsToAllMembers { hiddenapiFlags = "--no-force-assign-all" } @@ -351,7 +279,7 @@ func hiddenAPIEncodeDex(ctx android.ModuleContext, output android.WritablePath, Rule: hiddenAPIEncodeDexRule, Description: "hiddenapi encode dex", Input: dexInput, - Output: tmpOutput, + Output: encodeRuleOutput, Implicit: flagsCSV, Args: map[string]string{ "flagsCsv": flagsCSV.String(), @@ -362,8 +290,10 @@ func hiddenAPIEncodeDex(ctx android.ModuleContext, output android.WritablePath, }) if uncompressDex { - TransformZipAlign(ctx, output, tmpOutput) + TransformZipAlign(ctx, output, encodeRuleOutput) } + + return output } type hiddenApiAnnotationsDependencyTag struct { diff --git a/java/hiddenapi_modular.go b/java/hiddenapi_modular.go index 335f5b861..f2649d3c0 100644 --- a/java/hiddenapi_modular.go +++ b/java/hiddenapi_modular.go @@ -15,8 +15,12 @@ package java import ( + "fmt" + "strings" + "android/soong/android" "github.com/google/blueprint" + "github.com/google/blueprint/proptools" ) // Contains support for processing hiddenAPI in a modular fashion. @@ -63,6 +67,12 @@ var _ android.SdkMemberTypeDependencyTag = hiddenAPIStubsDependencyTag{} // hiddenAPIRelevantSdkKinds lists all the android.SdkKind instances that are needed by the hidden // API processing. +// +// These are in order from narrowest API surface to widest. Widest means the API stubs with the +// biggest API surface, e.g. test is wider than system is wider than public. Core platform is +// considered wider than test even though it has no relationship with test because the libraries +// that provide core platform API don't provide test. While the core platform API is being converted +// to a system API the system API is still a subset of core platform. var hiddenAPIRelevantSdkKinds = []android.SdkKind{ android.SdkPublic, android.SdkSystem, @@ -97,7 +107,12 @@ func hiddenAPIComputeMonolithicStubLibModules(config android.Config) map[android systemStubModules = append(systemStubModules, config.ProductHiddenAPIStubsSystem()...) testStubModules = append(testStubModules, config.ProductHiddenAPIStubsTest()...) if config.IsEnvTrue("EMMA_INSTRUMENT") { + // Add jacoco-stubs to public, system and test. It doesn't make any real difference as public + // allows everyone access but it is needed to ensure consistent flags between the + // bootclasspath fragment generated flags and the platform_bootclasspath generated flags. publicStubModules = append(publicStubModules, "jacoco-stubs") + systemStubModules = append(systemStubModules, "jacoco-stubs") + testStubModules = append(testStubModules, "jacoco-stubs") } m := map[android.SdkKind][]string{} @@ -119,23 +134,6 @@ func hiddenAPIAddStubLibDependencies(ctx android.BottomUpMutatorContext, sdkKind } } -// hiddenAPIGatherStubLibDexJarPaths gathers the paths to the dex jars from the dependencies added -// in hiddenAPIAddStubLibDependencies. -func hiddenAPIGatherStubLibDexJarPaths(ctx android.ModuleContext) map[android.SdkKind]android.Paths { - m := map[android.SdkKind]android.Paths{} - ctx.VisitDirectDepsIf(isActiveModule, func(module android.Module) { - tag := ctx.OtherModuleDependencyTag(module) - if hiddenAPIStubsTag, ok := tag.(hiddenAPIStubsDependencyTag); ok { - kind := hiddenAPIStubsTag.sdkKind - dexJar := hiddenAPIRetrieveDexJarBuildPath(ctx, module, kind) - if dexJar != nil { - m[kind] = append(m[kind], dexJar) - } - } - }) - return m -} - // hiddenAPIRetrieveDexJarBuildPath retrieves the DexJarBuildPath from the specified module, if // available, or reports an error. func hiddenAPIRetrieveDexJarBuildPath(ctx android.ModuleContext, module android.Module, kind android.SdkKind) android.Path { @@ -166,20 +164,36 @@ var sdkKindToHiddenapiListOption = map[android.SdkKind]string{ // // The rule is initialized but not built so that the caller can modify it and select an appropriate // name. -func ruleToGenerateHiddenAPIStubFlagsFile(ctx android.BuilderContext, outputPath android.OutputPath, bootDexJars android.Paths, sdkKindToPathList map[android.SdkKind]android.Paths) *android.RuleBuilder { +func ruleToGenerateHiddenAPIStubFlagsFile(ctx android.BuilderContext, outputPath android.WritablePath, bootDexJars android.Paths, input HiddenAPIFlagInput) *android.RuleBuilder { // Singleton rule which applies hiddenapi on all boot class path dex files. rule := android.NewRuleBuilder(pctx, ctx) tempPath := tempPathForRestat(ctx, outputPath) + // Find the widest API stubs provided by the fragments on which this depends, if any. + var dependencyStubDexJars android.Paths + for i := len(hiddenAPIRelevantSdkKinds) - 1; i >= 0; i-- { + kind := hiddenAPIRelevantSdkKinds[i] + stubsForKind := input.DependencyStubDexJarsByKind[kind] + if len(stubsForKind) != 0 { + dependencyStubDexJars = stubsForKind + break + } + } + command := rule.Command(). Tool(ctx.Config().HostToolPath(ctx, "hiddenapi")). Text("list"). + FlagForEachInput("--dependency-stub-dex=", dependencyStubDexJars). FlagForEachInput("--boot-dex=", bootDexJars) // Iterate over the sdk kinds in a fixed order. for _, sdkKind := range hiddenAPIRelevantSdkKinds { - paths := sdkKindToPathList[sdkKind] + // Merge in the stub dex jar paths for this kind from the fragments on which it depends. They + // will be needed to resolve dependencies from this fragment's stubs to classes in the other + // fragment's APIs. + dependencyPaths := input.DependencyStubDexJarsByKind[sdkKind] + paths := append(dependencyPaths, input.StubDexJarsByKind[sdkKind]...) if len(paths) > 0 { option := sdkKindToHiddenapiListOption[sdkKind] command.FlagWithInputList("--"+option+"=", paths, ":") @@ -233,15 +247,6 @@ type HiddenAPIFlagFileProperties struct { Unsupported_packages []string `android:"path"` } -func (p *HiddenAPIFlagFileProperties) hiddenAPIFlagFileInfo(ctx android.ModuleContext) hiddenAPIFlagFileInfo { - info := hiddenAPIFlagFileInfo{categoryToPaths: map[*hiddenAPIFlagFileCategory]android.Paths{}} - for _, category := range hiddenAPIFlagFileCategories { - paths := android.PathsForModuleSrc(ctx, category.propertyValueReader(p)) - info.categoryToPaths[category] = paths - } - return info -} - type hiddenAPIFlagFileCategory struct { // propertyName is the name of the property for this category. propertyName string @@ -255,6 +260,22 @@ type hiddenAPIFlagFileCategory struct { commandMutator func(command *android.RuleBuilderCommand, path android.Path) } +// The flag file category for removed members of the API. +// +// This is extracted from hiddenAPIFlagFileCategories as it is needed to add the dex signatures +// list of removed API members that are generated automatically from the removed.txt files provided +// by API stubs. +var hiddenAPIRemovedFlagFileCategory = &hiddenAPIFlagFileCategory{ + // See HiddenAPIFlagFileProperties.Removed + propertyName: "removed", + propertyValueReader: func(properties *HiddenAPIFlagFileProperties) []string { + return properties.Removed + }, + commandMutator: func(command *android.RuleBuilderCommand, path android.Path) { + command.FlagWithInput("--unsupported ", path).Flag("--ignore-conflicts ").FlagWithArg("--tag ", "removed") + }, +} + var hiddenAPIFlagFileCategories = []*hiddenAPIFlagFileCategory{ // See HiddenAPIFlagFileProperties.Unsupported { @@ -266,16 +287,7 @@ var hiddenAPIFlagFileCategories = []*hiddenAPIFlagFileCategory{ command.FlagWithInput("--unsupported ", path) }, }, - // See HiddenAPIFlagFileProperties.Removed - { - propertyName: "removed", - propertyValueReader: func(properties *HiddenAPIFlagFileProperties) []string { - return properties.Removed - }, - commandMutator: func(command *android.RuleBuilderCommand, path android.Path) { - command.FlagWithInput("--unsupported ", path).Flag("--ignore-conflicts ").FlagWithArg("--tag ", "removed") - }, - }, + hiddenAPIRemovedFlagFileCategory, // See HiddenAPIFlagFileProperties.Max_target_r_low_priority { propertyName: "max_target_r_low_priority", @@ -338,63 +350,445 @@ var hiddenAPIFlagFileCategories = []*hiddenAPIFlagFileCategory{ }, } -// hiddenAPIFlagFileInfo contains paths resolved from HiddenAPIFlagFileProperties -type hiddenAPIFlagFileInfo struct { - // categoryToPaths maps from the flag file category to the paths containing information for that - // category. - categoryToPaths map[*hiddenAPIFlagFileCategory]android.Paths -} +// FlagFilesByCategory maps a hiddenAPIFlagFileCategory to the paths to the files in that category. +type FlagFilesByCategory map[*hiddenAPIFlagFileCategory]android.Paths -func (i *hiddenAPIFlagFileInfo) append(other hiddenAPIFlagFileInfo) { +// append appends the supplied flags files to the corresponding category in this map. +func (s FlagFilesByCategory) append(other FlagFilesByCategory) { for _, category := range hiddenAPIFlagFileCategories { - i.categoryToPaths[category] = append(i.categoryToPaths[category], other.categoryToPaths[category]...) + s[category] = append(s[category], other[category]...) } } -var hiddenAPIFlagFileInfoProvider = blueprint.NewProvider(hiddenAPIFlagFileInfo{}) +// dedup removes duplicates in the flag files, while maintaining the order in which they were +// appended. +func (s FlagFilesByCategory) dedup() { + for category, paths := range s { + s[category] = android.FirstUniquePaths(paths) + } +} -// ruleToGenerateHiddenApiFlags creates a rule to create the monolithic hidden API flags from the -// flags from all the modules, the stub flags, augmented with some additional configuration files. +// HiddenAPIInfo contains information provided by the hidden API processing. // -// baseFlagsPath is the path to the flags file containing all the information from the stubs plus -// an entry for every single member in the dex implementation jars of the individual modules. Every -// signature in any of the other files MUST be included in this file. +// That includes paths resolved from HiddenAPIFlagFileProperties and also generated by hidden API +// processing. +type HiddenAPIInfo struct { + // FlagFilesByCategory maps from the flag file category to the paths containing information for + // that category. + FlagFilesByCategory FlagFilesByCategory + + // The paths to the stub dex jars for each of the android.SdkKind in hiddenAPIRelevantSdkKinds. + TransitiveStubDexJarsByKind StubDexJarsByKind + + // The output from the hidden API processing needs to be made available to other modules. + HiddenAPIFlagOutput +} + +func newHiddenAPIInfo() *HiddenAPIInfo { + info := HiddenAPIInfo{ + FlagFilesByCategory: FlagFilesByCategory{}, + TransitiveStubDexJarsByKind: StubDexJarsByKind{}, + } + return &info +} + +func (i *HiddenAPIInfo) mergeFromFragmentDeps(ctx android.ModuleContext, fragments []android.Module) { + // Merge all the information from the fragments. The fragments form a DAG so it is possible that + // this will introduce duplicates so they will be resolved after processing all the fragments. + for _, fragment := range fragments { + if ctx.OtherModuleHasProvider(fragment, HiddenAPIInfoProvider) { + info := ctx.OtherModuleProvider(fragment, HiddenAPIInfoProvider).(HiddenAPIInfo) + i.TransitiveStubDexJarsByKind.append(info.TransitiveStubDexJarsByKind) + } + } + + // Dedup and sort paths. + i.TransitiveStubDexJarsByKind.dedupAndSort() +} + +var HiddenAPIInfoProvider = blueprint.NewProvider(HiddenAPIInfo{}) + +// StubDexJarsByKind maps an android.SdkKind to the paths to stub dex jars appropriate for that +// level. See hiddenAPIRelevantSdkKinds for a list of the acceptable android.SdkKind values. +type StubDexJarsByKind map[android.SdkKind]android.Paths + +// append appends the supplied kind specific stub dex jar pargs to the corresponding kind in this +// map. +func (s StubDexJarsByKind) append(other StubDexJarsByKind) { + for _, kind := range hiddenAPIRelevantSdkKinds { + s[kind] = append(s[kind], other[kind]...) + } +} + +// dedupAndSort removes duplicates in the stub dex jar paths and sorts them into a consistent and +// deterministic order. +func (s StubDexJarsByKind) dedupAndSort() { + for kind, paths := range s { + s[kind] = android.SortedUniquePaths(paths) + } +} + +// HiddenAPIFlagInput encapsulates information obtained from a module and its dependencies that are +// needed for hidden API flag generation. +type HiddenAPIFlagInput struct { + // FlagFilesByCategory contains the flag files that override the initial flags that are derived + // from the stub dex files. + FlagFilesByCategory FlagFilesByCategory + + // StubDexJarsByKind contains the stub dex jars for different android.SdkKind and which determine + // the initial flags for each dex member. + StubDexJarsByKind StubDexJarsByKind + + // DependencyStubDexJarsByKind contains the stub dex jars provided by the fragments on which this + // depends. It is the result of merging HiddenAPIInfo.TransitiveStubDexJarsByKind from each + // fragment on which this depends. + DependencyStubDexJarsByKind StubDexJarsByKind + + // RemovedTxtFiles is the list of removed.txt files provided by java_sdk_library modules that are + // specified in the bootclasspath_fragment's stub_libs and contents properties. + RemovedTxtFiles android.Paths +} + +// newHiddenAPIFlagInput creates a new initialize HiddenAPIFlagInput struct. +func newHiddenAPIFlagInput() HiddenAPIFlagInput { + input := HiddenAPIFlagInput{ + FlagFilesByCategory: FlagFilesByCategory{}, + StubDexJarsByKind: StubDexJarsByKind{}, + } + + return input +} + +// canPerformHiddenAPIProcessing determines whether hidden API processing should be performed. // -// moduleSpecificFlagsPaths are the paths to the flags files generated by each module using -// information from the baseFlagsPath as well as from annotations within the source. +// A temporary workaround to avoid existing bootclasspath_fragments that do not provide the +// appropriate information needed for hidden API processing breaking the build. +// TODO(b/179354495): Remove this workaround. +func (i *HiddenAPIFlagInput) canPerformHiddenAPIProcessing(ctx android.ModuleContext, properties bootclasspathFragmentProperties) bool { + // Performing hidden API processing without stubs is not supported and it is unlikely to ever be + // required as the whole point of adding something to the bootclasspath fragment is to add it to + // the bootclasspath in order to be used by something else in the system. Without any stubs it + // cannot do that. + if len(i.StubDexJarsByKind) == 0 { + return false + } + + // Hidden API processing is always enabled in tests. + if ctx.Config().TestProductVariables != nil { + return true + } + + // A module that has fragments should have access to the information it needs in order to perform + // hidden API processing. + if len(properties.Fragments) != 0 { + return true + } + + // The art bootclasspath fragment does not depend on any other fragments but already supports + // hidden API processing. + imageName := proptools.String(properties.Image_name) + if imageName == "art" { + return true + } + + // Disable it for everything else. + return false +} + +// gatherStubLibInfo gathers information from the stub libs needed by hidden API processing from the +// dependencies added in hiddenAPIAddStubLibDependencies. +// +// That includes paths to the stub dex jars as well as paths to the *removed.txt files. +func (i *HiddenAPIFlagInput) gatherStubLibInfo(ctx android.ModuleContext, contents []android.Module) { + addFromModule := func(ctx android.ModuleContext, module android.Module, kind android.SdkKind) { + dexJar := hiddenAPIRetrieveDexJarBuildPath(ctx, module, kind) + if dexJar != nil { + i.StubDexJarsByKind[kind] = append(i.StubDexJarsByKind[kind], dexJar) + } + + if sdkLibrary, ok := module.(SdkLibraryDependency); ok { + removedTxtFile := sdkLibrary.SdkRemovedTxtFile(ctx, kind) + i.RemovedTxtFiles = append(i.RemovedTxtFiles, removedTxtFile.AsPaths()...) + } + } + + // If the contents includes any java_sdk_library modules then add them to the stubs. + for _, module := range contents { + if _, ok := module.(SdkLibraryDependency); ok { + // Add information for every possible kind needed by hidden API. SdkCorePlatform is not used + // as the java_sdk_library does not have special support for core_platform API, instead it is + // implemented as a customized form of SdkPublic. + for _, kind := range []android.SdkKind{android.SdkPublic, android.SdkSystem, android.SdkTest} { + addFromModule(ctx, module, kind) + } + } + } + + ctx.VisitDirectDepsIf(isActiveModule, func(module android.Module) { + tag := ctx.OtherModuleDependencyTag(module) + if hiddenAPIStubsTag, ok := tag.(hiddenAPIStubsDependencyTag); ok { + kind := hiddenAPIStubsTag.sdkKind + addFromModule(ctx, module, kind) + } + }) + + // Normalize the paths, i.e. remove duplicates and sort. + i.StubDexJarsByKind.dedupAndSort() + i.RemovedTxtFiles = android.SortedUniquePaths(i.RemovedTxtFiles) +} + +// extractFlagFilesFromProperties extracts the paths to flag files that are specified in the +// supplied properties and stores them in this struct. +func (i *HiddenAPIFlagInput) extractFlagFilesFromProperties(ctx android.ModuleContext, p *HiddenAPIFlagFileProperties) { + for _, category := range hiddenAPIFlagFileCategories { + paths := android.PathsForModuleSrc(ctx, category.propertyValueReader(p)) + i.FlagFilesByCategory[category] = paths + } +} + +func (i *HiddenAPIFlagInput) transitiveStubDexJarsByKind() StubDexJarsByKind { + transitive := i.DependencyStubDexJarsByKind + transitive.append(i.StubDexJarsByKind) + return transitive +} + +// HiddenAPIFlagOutput contains paths to output files from the hidden API flag generation for a +// bootclasspath_fragment module. +type HiddenAPIFlagOutput struct { + // The path to the generated stub-flags.csv file. + StubFlagsPath android.Path + + // The path to the generated annotation-flags.csv file. + AnnotationFlagsPath android.Path + + // The path to the generated metadata.csv file. + MetadataPath android.Path + + // The path to the generated index.csv file. + IndexPath android.Path + + // The path to the generated all-flags.csv file. + AllFlagsPath android.Path +} + +// pathForValidation creates a path of the same type as the supplied type but with a name of +// <path>.valid. // -// augmentationInfo is a struct containing paths to files that augment the information provided by -// the moduleSpecificFlagsPaths. -// ruleToGenerateHiddenApiFlags creates a rule to create the monolithic hidden API flags from the -// flags from all the modules, the stub flags, augmented with some additional configuration files. +// e.g. If path is an OutputPath for out/soong/hiddenapi/hiddenapi-flags.csv then this will return +// an OutputPath for out/soong/hiddenapi/hiddenapi-flags.csv.valid +func pathForValidation(ctx android.PathContext, path android.WritablePath) android.WritablePath { + extWithoutLeadingDot := strings.TrimPrefix(path.Ext(), ".") + return path.ReplaceExtension(ctx, extWithoutLeadingDot+".valid") +} + +// buildRuleToGenerateHiddenApiFlags creates a rule to create the monolithic hidden API flags from +// the flags from all the modules, the stub flags, augmented with some additional configuration +// files. // // baseFlagsPath is the path to the flags file containing all the information from the stubs plus // an entry for every single member in the dex implementation jars of the individual modules. Every // signature in any of the other files MUST be included in this file. // -// moduleSpecificFlagsPaths are the paths to the flags files generated by each module using -// information from the baseFlagsPath as well as from annotations within the source. +// annotationFlags is the path to the annotation flags file generated from annotation information +// in each module. // -// augmentationInfo is a struct containing paths to files that augment the information provided by -// the moduleSpecificFlagsPaths. -func ruleToGenerateHiddenApiFlags(ctx android.BuilderContext, outputPath android.WritablePath, baseFlagsPath android.Path, moduleSpecificFlagsPaths android.Paths, augmentationInfo hiddenAPIFlagFileInfo) { +// hiddenAPIInfo is a struct containing paths to files that augment the information provided by +// the annotationFlags. +func buildRuleToGenerateHiddenApiFlags(ctx android.BuilderContext, name, desc string, + outputPath android.WritablePath, baseFlagsPath android.Path, annotationFlags android.Path, + flagFilesByCategory FlagFilesByCategory, allFlagsPaths android.Paths, generatedRemovedDexSignatures android.OptionalPath) { + + // The file which is used to record that the flags file is valid. + var validFile android.WritablePath + + // If there are flag files that have been generated by fragments on which this depends then use + // them to validate the flag file generated by the rules created by this method. + if len(allFlagsPaths) > 0 { + // The flags file generated by the rule created by this method needs to be validated to ensure + // that it is consistent with the flag files generated by the individual fragments. + + validFile = pathForValidation(ctx, outputPath) + + // Create a rule to validate the output from the following rule. + rule := android.NewRuleBuilder(pctx, ctx) + rule.Command(). + BuiltTool("verify_overlaps"). + Input(outputPath). + Inputs(allFlagsPaths). + // If validation passes then update the file that records that. + Text("&& touch").Output(validFile) + rule.Build(name+"Validation", desc+" validation") + } + + // Create the rule that will generate the flag files. tempPath := tempPathForRestat(ctx, outputPath) rule := android.NewRuleBuilder(pctx, ctx) command := rule.Command(). BuiltTool("generate_hiddenapi_lists"). FlagWithInput("--csv ", baseFlagsPath). - Inputs(moduleSpecificFlagsPaths). + Input(annotationFlags). FlagWithOutput("--output ", tempPath) // Add the options for the different categories of flag files. for _, category := range hiddenAPIFlagFileCategories { - paths := augmentationInfo.categoryToPaths[category] + paths := flagFilesByCategory[category] for _, path := range paths { category.commandMutator(command, path) } } + // If available then pass the automatically generated file containing dex signatures of removed + // API members to the rule so they can be marked as removed. + if generatedRemovedDexSignatures.Valid() { + hiddenAPIRemovedFlagFileCategory.commandMutator(command, generatedRemovedDexSignatures.Path()) + } + commitChangeForRestat(rule, tempPath, outputPath) - rule.Build("hiddenAPIFlagsFile", "hiddenapi flags") + if validFile != nil { + // Add the file that indicates that the file generated by this is valid. + // + // This will cause the validation rule above to be run any time that the output of this rule + // changes but the validation will run in parallel with other rules that depend on this file. + command.Validation(validFile) + } + + rule.Build(name, desc) +} + +// hiddenAPIGenerateAllFlagsForBootclasspathFragment will generate all the flags for a fragment +// of the bootclasspath. +// +// It takes: +// * Map from android.SdkKind to stub dex jar paths defining the API for that sdk kind. +// * The list of modules that are the contents of the fragment. +// * The additional manually curated flag files to use. +// +// It generates: +// * stub-flags.csv +// * annotation-flags.csv +// * metadata.csv +// * index.csv +// * all-flags.csv +func hiddenAPIGenerateAllFlagsForBootclasspathFragment(ctx android.ModuleContext, contents []hiddenAPIModule, input HiddenAPIFlagInput) *HiddenAPIFlagOutput { + hiddenApiSubDir := "modular-hiddenapi" + + // Gather the dex files for the boot libraries provided by this fragment. + bootDexJars := extractBootDexJarsFromHiddenAPIModules(ctx, contents) + + // Generate the stub-flags.csv. + stubFlagsCSV := android.PathForModuleOut(ctx, hiddenApiSubDir, "stub-flags.csv") + rule := ruleToGenerateHiddenAPIStubFlagsFile(ctx, stubFlagsCSV, bootDexJars, input) + rule.Build("modularHiddenAPIStubFlagsFile", "modular hiddenapi stub flags") + + // Extract the classes jars from the contents. + classesJars := extractClassJarsFromHiddenAPIModules(ctx, contents) + + // Generate the set of flags from the annotations in the source code. + annotationFlagsCSV := android.PathForModuleOut(ctx, hiddenApiSubDir, "annotation-flags.csv") + buildRuleToGenerateAnnotationFlags(ctx, "modular hiddenapi annotation flags", classesJars, stubFlagsCSV, annotationFlagsCSV) + + // Generate the metadata from the annotations in the source code. + metadataCSV := android.PathForModuleOut(ctx, hiddenApiSubDir, "metadata.csv") + buildRuleToGenerateMetadata(ctx, "modular hiddenapi metadata", classesJars, stubFlagsCSV, metadataCSV) + + // Generate the index file from the CSV files in the classes jars. + indexCSV := android.PathForModuleOut(ctx, hiddenApiSubDir, "index.csv") + buildRuleToGenerateIndex(ctx, "modular hiddenapi index", classesJars, indexCSV) + + // Removed APIs need to be marked and in order to do that the hiddenAPIInfo needs to specify files + // containing dex signatures of all the removed APIs. In the monolithic files that is done by + // manually combining all the removed.txt files for each API and then converting them to dex + // signatures, see the combined-removed-dex module. This does that automatically by using the + // *removed.txt files retrieved from the java_sdk_library modules that are specified in the + // stub_libs and contents properties of a bootclasspath_fragment. + removedDexSignatures := buildRuleToGenerateRemovedDexSignatures(ctx, input.RemovedTxtFiles) + + // Generate the all-flags.csv which are the flags that will, in future, be encoded into the dex + // files. + outputPath := android.PathForModuleOut(ctx, hiddenApiSubDir, "all-flags.csv") + buildRuleToGenerateHiddenApiFlags(ctx, "modularHiddenApiAllFlags", "modular hiddenapi all flags", outputPath, stubFlagsCSV, annotationFlagsCSV, input.FlagFilesByCategory, nil, removedDexSignatures) + + // Store the paths in the info for use by other modules and sdk snapshot generation. + output := HiddenAPIFlagOutput{ + StubFlagsPath: stubFlagsCSV, + AnnotationFlagsPath: annotationFlagsCSV, + MetadataPath: metadataCSV, + IndexPath: indexCSV, + AllFlagsPath: outputPath, + } + return &output +} + +func buildRuleToGenerateRemovedDexSignatures(ctx android.ModuleContext, removedTxtFiles android.Paths) android.OptionalPath { + if len(removedTxtFiles) == 0 { + return android.OptionalPath{} + } + + output := android.PathForModuleOut(ctx, "modular-hiddenapi/removed-dex-signatures.txt") + + rule := android.NewRuleBuilder(pctx, ctx) + rule.Command(). + BuiltTool("metalava"). + Flag("--no-banner"). + Inputs(removedTxtFiles). + FlagWithOutput("--dex-api ", output) + rule.Build("modular-hiddenapi-removed-dex-signatures", "modular hiddenapi removed dex signatures") + return android.OptionalPathForPath(output) +} + +// gatherHiddenAPIModuleFromContents gathers the hiddenAPIModule from the supplied contents. +func gatherHiddenAPIModuleFromContents(ctx android.ModuleContext, contents []android.Module) []hiddenAPIModule { + hiddenAPIModules := []hiddenAPIModule{} + for _, module := range contents { + if hiddenAPI, ok := module.(hiddenAPIModule); ok { + hiddenAPIModules = append(hiddenAPIModules, hiddenAPI) + } else if _, ok := module.(*DexImport); ok { + // Ignore this for the purposes of hidden API processing + } else { + ctx.ModuleErrorf("module %s does not implement hiddenAPIModule", module) + } + } + return hiddenAPIModules +} + +// extractBootDexJarsFromHiddenAPIModules extracts the boot dex jars from the supplied modules. +func extractBootDexJarsFromHiddenAPIModules(ctx android.ModuleContext, contents []hiddenAPIModule) android.Paths { + bootDexJars := android.Paths{} + for _, module := range contents { + bootDexJar := module.bootDexJar() + if bootDexJar == nil { + if ctx.Config().AlwaysUsePrebuiltSdks() { + // TODO(b/179354495): Remove this work around when it is unnecessary. + // Prebuilt modules like framework-wifi do not yet provide dex implementation jars. So, + // create a fake one that will cause a build error only if it is used. + fake := android.PathForModuleOut(ctx, "fake/boot-dex/%s.jar", module.Name()) + + // Create an error rule that pretends to create the output file but will actually fail if it + // is run. + ctx.Build(pctx, android.BuildParams{ + Rule: android.ErrorRule, + Output: fake, + Args: map[string]string{ + "error": fmt.Sprintf("missing dependencies: boot dex jar for %s", module), + }, + }) + bootDexJars = append(bootDexJars, fake) + } else { + ctx.ModuleErrorf("module %s does not provide a dex jar", module) + } + } else { + bootDexJars = append(bootDexJars, bootDexJar) + } + } + return bootDexJars +} + +// extractClassJarsFromHiddenAPIModules extracts the class jars from the supplied modules. +func extractClassJarsFromHiddenAPIModules(ctx android.ModuleContext, contents []hiddenAPIModule) android.Paths { + classesJars := android.Paths{} + for _, module := range contents { + classesJars = append(classesJars, module.classesJars()...) + } + return classesJars } diff --git a/java/hiddenapi_monolithic.go b/java/hiddenapi_monolithic.go new file mode 100644 index 000000000..a6bf8c705 --- /dev/null +++ b/java/hiddenapi_monolithic.go @@ -0,0 +1,102 @@ +// Copyright (C) 2021 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package java + +import ( + "android/soong/android" + "github.com/google/blueprint" +) + +// MonolithicHiddenAPIInfo contains information needed/provided by the hidden API generation of the +// monolithic hidden API files. +// +// Each list of paths includes all the equivalent paths from each of the bootclasspath_fragment +// modules that contribute to the platform-bootclasspath. +type MonolithicHiddenAPIInfo struct { + // FlagsFilesByCategory maps from the flag file category to the paths containing information for + // that category. + FlagsFilesByCategory FlagFilesByCategory + + // The paths to the generated stub-flags.csv files. + StubFlagsPaths android.Paths + + // The paths to the generated annotation-flags.csv files. + AnnotationFlagsPaths android.Paths + + // The paths to the generated metadata.csv files. + MetadataPaths android.Paths + + // The paths to the generated index.csv files. + IndexPaths android.Paths + + // The paths to the generated all-flags.csv files. + AllFlagsPaths android.Paths +} + +// newMonolithicHiddenAPIInfo creates a new MonolithicHiddenAPIInfo from the flagFilesByCategory +// plus information provided by each of the fragments. +func newMonolithicHiddenAPIInfo(ctx android.ModuleContext, flagFilesByCategory FlagFilesByCategory, fragments []android.Module) MonolithicHiddenAPIInfo { + monolithicInfo := MonolithicHiddenAPIInfo{} + + monolithicInfo.FlagsFilesByCategory = flagFilesByCategory + + // Merge all the information from the fragments. The fragments form a DAG so it is possible that + // this will introduce duplicates so they will be resolved after processing all the fragments. + for _, fragment := range fragments { + if ctx.OtherModuleHasProvider(fragment, HiddenAPIInfoProvider) { + info := ctx.OtherModuleProvider(fragment, HiddenAPIInfoProvider).(HiddenAPIInfo) + monolithicInfo.append(&info) + } + } + + // Dedup paths. + monolithicInfo.dedup() + + return monolithicInfo +} + +// append appends all the files from the supplied info to the corresponding files in this struct. +func (i *MonolithicHiddenAPIInfo) append(other *HiddenAPIInfo) { + i.FlagsFilesByCategory.append(other.FlagFilesByCategory) + + // The output may not be set if the bootclasspath_fragment has not yet been updated to support + // hidden API processing. + // TODO(b/179354495): Switch back to append once all bootclasspath_fragment modules have been + // updated to support hidden API processing properly. + appendIfNotNil := func(paths android.Paths, path android.Path) android.Paths { + if path == nil { + return paths + } + return append(paths, path) + } + i.StubFlagsPaths = appendIfNotNil(i.StubFlagsPaths, other.StubFlagsPath) + i.AnnotationFlagsPaths = appendIfNotNil(i.AnnotationFlagsPaths, other.AnnotationFlagsPath) + i.MetadataPaths = appendIfNotNil(i.MetadataPaths, other.MetadataPath) + i.IndexPaths = appendIfNotNil(i.IndexPaths, other.IndexPath) + i.AllFlagsPaths = appendIfNotNil(i.AllFlagsPaths, other.AllFlagsPath) +} + +// dedup removes duplicates in all the paths, while maintaining the order in which they were +// appended. +func (i *MonolithicHiddenAPIInfo) dedup() { + i.FlagsFilesByCategory.dedup() + i.StubFlagsPaths = android.FirstUniquePaths(i.StubFlagsPaths) + i.AnnotationFlagsPaths = android.FirstUniquePaths(i.AnnotationFlagsPaths) + i.MetadataPaths = android.FirstUniquePaths(i.MetadataPaths) + i.IndexPaths = android.FirstUniquePaths(i.IndexPaths) + i.AllFlagsPaths = android.FirstUniquePaths(i.AllFlagsPaths) +} + +var monolithicHiddenAPIInfoProvider = blueprint.NewProvider(MonolithicHiddenAPIInfo{}) diff --git a/java/hiddenapi_singleton.go b/java/hiddenapi_singleton.go index f6af50132..bdf055abc 100644 --- a/java/hiddenapi_singleton.go +++ b/java/hiddenapi_singleton.go @@ -117,7 +117,6 @@ func hiddenAPISingletonFactory() android.Singleton { } type hiddenAPISingleton struct { - flags android.Path } // hiddenAPI singleton rules @@ -136,24 +135,15 @@ func (h *hiddenAPISingleton) GenerateBuildActions(ctx android.SingletonContext) // consistency. if ctx.Config().PrebuiltHiddenApiDir(ctx) != "" { - h.flags = prebuiltFlagsRule(ctx) + prebuiltFlagsRule(ctx) prebuiltIndexRule(ctx) return } - - // These rules depend on files located in frameworks/base, skip them if running in a tree that doesn't have them. - if ctx.Config().FrameworksBaseDirExists(ctx) { - h.flags = flagsRule(ctx) - } else { - h.flags = emptyFlagsRule(ctx) - } } // Checks to see whether the supplied module variant is in the list of boot jars. // -// Apart from the context this is identical to isModuleInConfiguredListForSingleton. -// -// TODO(b/179354495): Avoid having to perform this type of check or if necessary dedup it. +// TODO(b/179354495): Avoid having to perform this type of check. func isModuleInConfiguredList(ctx android.BaseModuleContext, module android.Module, configuredBootJars android.ConfiguredJarList) bool { name := ctx.OtherModuleName(module) @@ -177,11 +167,11 @@ func isModuleInConfiguredList(ctx android.BaseModuleContext, module android.Modu // Now match the apex part of the boot image configuration. requiredApex := configuredBootJars.Apex(index) if requiredApex == "platform" || requiredApex == "system_ext" { - if len(apexInfo.InApexes) != 0 { + if len(apexInfo.InApexVariants) != 0 { // A platform variant is required but this is for an apex so ignore it. return false } - } else if !apexInfo.InApexByBaseName(requiredApex) { + } else if !apexInfo.InApexVariantByBaseName(requiredApex) { // An apex variant for a specific apex is required but this is the wrong apex. return false } @@ -189,7 +179,7 @@ func isModuleInConfiguredList(ctx android.BaseModuleContext, module android.Modu return true } -func prebuiltFlagsRule(ctx android.SingletonContext) android.Path { +func prebuiltFlagsRule(ctx android.SingletonContext) { outputPath := hiddenAPISingletonPaths(ctx).flags inputPath := android.PathForSource(ctx, ctx.Config().PrebuiltHiddenApiDir(ctx), "hiddenapi-flags.csv") @@ -198,8 +188,6 @@ func prebuiltFlagsRule(ctx android.SingletonContext) android.Path { Output: outputPath, Input: inputPath, }) - - return outputPath } func prebuiltIndexRule(ctx android.SingletonContext) { @@ -213,28 +201,6 @@ func prebuiltIndexRule(ctx android.SingletonContext) { }) } -// flagsRule is a placeholder that simply returns the location of the file, the generation of the -// ninja rules is done in generateHiddenAPIBuildActions. -func flagsRule(ctx android.SingletonContext) android.Path { - outputPath := hiddenAPISingletonPaths(ctx).flags - return outputPath -} - -// emptyFlagsRule creates a rule to build an empty hiddenapi-flags.csv, which is needed by master-art-host builds that -// have a partial manifest without frameworks/base but still need to build a boot image. -func emptyFlagsRule(ctx android.SingletonContext) android.Path { - rule := android.NewRuleBuilder(pctx, ctx) - - outputPath := hiddenAPISingletonPaths(ctx).flags - - rule.Command().Text("rm").Flag("-f").Output(outputPath) - rule.Command().Text("touch").Output(outputPath) - - rule.Build("emptyHiddenAPIFlagsFile", "empty hiddenapi flags") - - return outputPath -} - // tempPathForRestat creates a path of the same type as the supplied type but with a name of // <path>.tmp. // diff --git a/java/hiddenapi_singleton_test.go b/java/hiddenapi_singleton_test.go index 3ab22772a..dcd363c2c 100644 --- a/java/hiddenapi_singleton_test.go +++ b/java/hiddenapi_singleton_test.go @@ -60,10 +60,7 @@ func TestHiddenAPISingleton(t *testing.T) { } func TestHiddenAPISingletonWithSourceAndPrebuiltPreferredButNoDex(t *testing.T) { - expectedErrorMessage := - "hiddenapi has determined that the source module \"foo\" should be ignored as it has been" + - " replaced by the prebuilt module \"prebuilt_foo\" but unfortunately it does not provide a" + - " suitable boot dex jar" + expectedErrorMessage := "module prebuilt_foo{os:android,arch:common} does not provide a dex jar" android.GroupFixturePreparers( hiddenApiFixtureFactory, @@ -277,3 +274,56 @@ func TestHiddenAPISingletonWithPrebuiltCsvFile(t *testing.T) { android.AssertStringEquals(t, "hiddenapi encode dex rule flags csv", expectedFlagsCsv, actualFlagsCsv) } + +func TestHiddenAPIEncoding_JavaSdkLibrary(t *testing.T) { + + result := android.GroupFixturePreparers( + hiddenApiFixtureFactory, + FixtureConfigureBootJars("platform:foo"), + PrepareForTestWithJavaSdkLibraryFiles, + FixtureWithLastReleaseApis("foo"), + + // Make sure that the frameworks/base/Android.bp file exists as otherwise hidden API encoding + // is disabled. + android.FixtureAddTextFile("frameworks/base/Android.bp", ""), + ).RunTestWithBp(t, ` + java_sdk_library { + name: "foo", + srcs: ["a.java"], + shared_library: false, + compile_dex: true, + public: {enabled: true}, + } + `) + + checkDexEncoded := func(t *testing.T, name, unencodedDexJar, encodedDexJar string) { + moduleForTests := result.ModuleForTests(name, "android_common") + + encodeDexRule := moduleForTests.Rule("hiddenAPIEncodeDex") + actualUnencodedDexJar := encodeDexRule.Input + + // Make sure that the module has its dex jar encoded. + android.AssertStringEquals(t, "encode embedded java_library", unencodedDexJar, actualUnencodedDexJar.String()) + + // Make sure that the encoded dex jar is the exported one. + exportedDexJar := moduleForTests.Module().(UsesLibraryDependency).DexJarBuildPath() + android.AssertPathRelativeToTopEquals(t, "encode embedded java_library", encodedDexJar, exportedDexJar) + } + + // The java_library embedded with the java_sdk_library must be dex encoded. + t.Run("foo", func(t *testing.T) { + expectedUnencodedDexJar := "out/soong/.intermediates/foo/android_common/aligned/foo.jar" + expectedEncodedDexJar := "out/soong/.intermediates/foo/android_common/hiddenapi/foo.jar" + checkDexEncoded(t, "foo", expectedUnencodedDexJar, expectedEncodedDexJar) + }) + + // The dex jar of the child implementation java_library of the java_sdk_library is not currently + // dex encoded. + t.Run("foo.impl", func(t *testing.T) { + fooImpl := result.ModuleForTests("foo.impl", "android_common") + encodeDexRule := fooImpl.MaybeRule("hiddenAPIEncodeDex") + if encodeDexRule.Rule != nil { + t.Errorf("foo.impl is not expected to be encoded") + } + }) +} diff --git a/java/java.go b/java/java.go index 9a5fbfc35..2bbb5b102 100644 --- a/java/java.go +++ b/java/java.go @@ -57,6 +57,10 @@ func registerJavaBuildComponents(ctx android.RegistrationContext) { ctx.RegisterModuleType("java_host_for_device", HostForDeviceFactory) ctx.RegisterModuleType("dex_import", DexImportFactory) + // This mutator registers dependencies on dex2oat for modules that should be + // dexpreopted. This is done late when the final variants have been + // established, to not get the dependencies split into the wrong variants and + // to support the checks in dexpreoptDisabled(). ctx.FinalDepsMutators(func(ctx android.RegisterMutatorsContext) { ctx.BottomUp("dexpreopt_tool_deps", dexpreoptToolDepsMutator).Parallel() }) @@ -481,11 +485,6 @@ func shouldUncompressDex(ctx android.ModuleContext, dexpreopter *dexpreopter) bo } func (j *Library) GenerateAndroidBuildActions(ctx android.ModuleContext) { - // Initialize the hiddenapi structure. Pass in the configuration name rather than the module name - // so the hidden api will encode the <x>.impl java_ library created by java_sdk_library just as it - // would the <x> library if <x> was configured as a boot jar. - j.initHiddenAPI(ctx, j.ConfigurationName()) - j.sdkVersion = j.SdkVersion(ctx) j.minSdkVersion = j.MinSdkVersion(ctx) @@ -1241,9 +1240,6 @@ func (j *Import) GenerateAndroidBuildActions(ctx android.ModuleContext) { j.sdkVersion = j.SdkVersion(ctx) j.minSdkVersion = j.MinSdkVersion(ctx) - // Initialize the hiddenapi structure. - j.initHiddenAPI(ctx, j.BaseModuleName()) - if !ctx.Provider(android.ApexInfoProvider).(android.ApexInfo).IsForPlatform() { j.hideApexVariantFromMake = true } @@ -1315,7 +1311,9 @@ func (j *Import) GenerateAndroidBuildActions(ctx android.ModuleContext) { di := ctx.OtherModuleProvider(deapexerModule, android.DeapexerProvider).(android.DeapexerInfo) if dexOutputPath := di.PrebuiltExportPath(j.BaseModuleName(), ".dexjar"); dexOutputPath != nil { j.dexJarFile = dexOutputPath - j.hiddenAPIExtractInformation(ctx, dexOutputPath, outputFile) + + // Initialize the hiddenapi structure. + j.initHiddenAPI(ctx, dexOutputPath, outputFile, nil) } else { // This should never happen as a variant for a prebuilt_apex is only created if the // prebuilt_apex has been configured to export the java library dex file. @@ -1346,9 +1344,11 @@ func (j *Import) GenerateAndroidBuildActions(ctx android.ModuleContext) { return } - // Hidden API CSV generation and dex encoding - dexOutputFile = j.hiddenAPIExtractAndEncode(ctx, dexOutputFile, outputFile, - proptools.Bool(j.dexProperties.Uncompress_dex)) + // Initialize the hiddenapi structure. + j.initHiddenAPI(ctx, dexOutputFile, outputFile, j.dexProperties.Uncompress_dex) + + // Encode hidden API flags in dex file. + dexOutputFile = j.hiddenAPIEncodeDex(ctx, dexOutputFile) j.dexJarFile = dexOutputFile } diff --git a/java/platform_bootclasspath.go b/java/platform_bootclasspath.go index c787e4797..87c695cb5 100644 --- a/java/platform_bootclasspath.go +++ b/java/platform_bootclasspath.go @@ -196,25 +196,18 @@ func (b *platformBootclasspathModule) GenerateAndroidBuildActions(ctx android.Mo return } - b.generateBootImageBuildActions(ctx, updatableModules) + b.generateBootImageBuildActions(ctx, nonUpdatableModules, updatableModules) } // Generate classpaths.proto config func (b *platformBootclasspathModule) generateClasspathProtoBuildActions(ctx android.ModuleContext) { // ART and platform boot jars must have a corresponding entry in DEX2OATBOOTCLASSPATH classpathJars := configuredJarListToClasspathJars(ctx, b.ClasspathFragmentToConfiguredJarList(ctx), BOOTCLASSPATH, DEX2OATBOOTCLASSPATH) - - // TODO(satayev): remove updatable boot jars once each apex has its own fragment - global := dexpreopt.GetGlobalConfig(ctx) - classpathJars = append(classpathJars, configuredJarListToClasspathJars(ctx, global.UpdatableBootJars, BOOTCLASSPATH)...) - b.classpathFragmentBase().generateClasspathProtoBuildActions(ctx, classpathJars) } func (b *platformBootclasspathModule) ClasspathFragmentToConfiguredJarList(ctx android.ModuleContext) android.ConfiguredJarList { - global := dexpreopt.GetGlobalConfig(ctx) - // TODO(satayev): split ART apex jars into their own classpathFragment - return global.BootJars + return b.getImageConfig(ctx).modules } // checkNonUpdatableModules ensures that the non-updatable modules supplied are not part of an @@ -225,7 +218,7 @@ func (b *platformBootclasspathModule) checkNonUpdatableModules(ctx android.Modul fromUpdatableApex := apexInfo.Updatable if fromUpdatableApex { // error: this jar is part of an updatable apex - ctx.ModuleErrorf("module %q from updatable apexes %q is not allowed in the framework boot image", ctx.OtherModuleName(m), apexInfo.InApexes) + ctx.ModuleErrorf("module %q from updatable apexes %q is not allowed in the framework boot image", ctx.OtherModuleName(m), apexInfo.InApexVariants) } else { // ok: this jar is part of the platform or a non-updatable apex } @@ -242,8 +235,15 @@ func (b *platformBootclasspathModule) checkUpdatableModules(ctx android.ModuleCo } else { name := ctx.OtherModuleName(m) if apexInfo.IsForPlatform() { - // error: this jar is part of the platform - ctx.ModuleErrorf("module %q from platform is not allowed in the updatable boot jars list", name) + // If AlwaysUsePrebuiltSdks() returns true then it is possible that the updatable list will + // include platform variants of a prebuilt module due to workarounds elsewhere. In that case + // do not treat this as an error. + // TODO(b/179354495): Always treat this as an error when migration to bootclasspath_fragment + // modules is complete. + if !ctx.Config().AlwaysUsePrebuiltSdks() { + // error: this jar is part of the platform + ctx.ModuleErrorf("module %q from platform is not allowed in the updatable boot jars list", name) + } } else { // TODO(b/177892522): Treat this as an error. // Cannot do that at the moment because framework-wifi and framework-tethering are in the @@ -257,17 +257,6 @@ func (b *platformBootclasspathModule) getImageConfig(ctx android.EarlyModuleCont return defaultBootImageConfig(ctx) } -// hiddenAPISupportingModule encapsulates the information provided by any module that contributes to -// the hidden API processing. -type hiddenAPISupportingModule struct { - module android.Module - - bootDexJar android.Path - flagsCSV android.Path - indexCSV android.Path - metadataCSV android.Path -} - // generateHiddenAPIBuildActions generates all the hidden API related build rules. func (b *platformBootclasspathModule) generateHiddenAPIBuildActions(ctx android.ModuleContext, modules []android.Module, fragments []android.Module) { @@ -290,133 +279,82 @@ func (b *platformBootclasspathModule) generateHiddenAPIBuildActions(ctx android. return } - // nilPathHandler will check the supplied path and if it is nil then it will either immediately - // report an error, or it will defer the error reporting until it is actually used, depending - // whether missing dependencies are allowed. - var nilPathHandler func(path android.Path, name string, module android.Module) android.Path - if ctx.Config().AllowMissingDependencies() { - nilPathHandler = func(path android.Path, name string, module android.Module) android.Path { - if path == nil { - outputPath := android.PathForModuleOut(ctx, "missing", module.Name(), name) - path = outputPath - - // Create an error rule that pretends to create the output file but will actually fail if it - // is run. - ctx.Build(pctx, android.BuildParams{ - Rule: android.ErrorRule, - Output: outputPath, - Args: map[string]string{ - "error": fmt.Sprintf("missing hidden API file: %s for %s", name, module), - }, - }) - } - return path - } - } else { - nilPathHandler = func(path android.Path, name string, module android.Module) android.Path { - if path == nil { - ctx.ModuleErrorf("module %s does not provide a %s file", module, name) - } - return path - } - } + monolithicInfo := b.createAndProvideMonolithicHiddenAPIInfo(ctx, fragments) - hiddenAPISupportingModules := []hiddenAPISupportingModule{} - for _, module := range modules { - if h, ok := module.(hiddenAPIIntf); ok { - hiddenAPISupportingModule := hiddenAPISupportingModule{ - module: module, - bootDexJar: nilPathHandler(h.bootDexJar(), "bootDexJar", module), - flagsCSV: nilPathHandler(h.flagsCSV(), "flagsCSV", module), - indexCSV: nilPathHandler(h.indexCSV(), "indexCSV", module), - metadataCSV: nilPathHandler(h.metadataCSV(), "metadataCSV", module), - } + // Create the input to pass to ruleToGenerateHiddenAPIStubFlagsFile + input := newHiddenAPIFlagInput() - // If any errors were reported when trying to populate the hiddenAPISupportingModule struct - // then don't add it to the list. - if ctx.Failed() { - continue - } + // Gather stub library information from the dependencies on modules provided by + // hiddenAPIComputeMonolithicStubLibModules. + input.gatherStubLibInfo(ctx, nil) - hiddenAPISupportingModules = append(hiddenAPISupportingModules, hiddenAPISupportingModule) - } else if _, ok := module.(*DexImport); ok { - // Ignore this for the purposes of hidden API processing - } else { - ctx.ModuleErrorf("module %s of type %s does not support hidden API processing", module, ctx.OtherModuleType(module)) - } - } + // Use the flag files from this module and all the fragments. + input.FlagFilesByCategory = monolithicInfo.FlagsFilesByCategory - moduleSpecificFlagsPaths := android.Paths{} - for _, module := range hiddenAPISupportingModules { - moduleSpecificFlagsPaths = append(moduleSpecificFlagsPaths, module.flagsCSV) - } + hiddenAPIModules := gatherHiddenAPIModuleFromContents(ctx, modules) - flagFileInfo := b.properties.Hidden_api.hiddenAPIFlagFileInfo(ctx) - for _, fragment := range fragments { - if ctx.OtherModuleHasProvider(fragment, hiddenAPIFlagFileInfoProvider) { - info := ctx.OtherModuleProvider(fragment, hiddenAPIFlagFileInfoProvider).(hiddenAPIFlagFileInfo) - flagFileInfo.append(info) - } - } + // Generate the monolithic stub-flags.csv file. + bootDexJars := extractBootDexJarsFromHiddenAPIModules(ctx, hiddenAPIModules) + stubFlags := hiddenAPISingletonPaths(ctx).stubFlags + rule := ruleToGenerateHiddenAPIStubFlagsFile(ctx, stubFlags, bootDexJars, input) + rule.Build("platform-bootclasspath-monolithic-hiddenapi-stub-flags", "monolithic hidden API stub flags") - // Store the information for testing. - ctx.SetProvider(hiddenAPIFlagFileInfoProvider, flagFileInfo) + // Extract the classes jars from the contents. + classesJars := extractClassJarsFromHiddenAPIModules(ctx, hiddenAPIModules) - outputPath := hiddenAPISingletonPaths(ctx).flags - baseFlagsPath := hiddenAPISingletonPaths(ctx).stubFlags - ruleToGenerateHiddenApiFlags(ctx, outputPath, baseFlagsPath, moduleSpecificFlagsPaths, flagFileInfo) + // Generate the annotation-flags.csv file from all the module annotations. + annotationFlags := android.PathForModuleOut(ctx, "hiddenapi-monolithic", "annotation-flags.csv") + buildRuleToGenerateAnnotationFlags(ctx, "monolithic hiddenapi flags", classesJars, stubFlags, annotationFlags) - b.generateHiddenAPIStubFlagsRules(ctx, hiddenAPISupportingModules) - b.generateHiddenAPIIndexRules(ctx, hiddenAPISupportingModules) - b.generatedHiddenAPIMetadataRules(ctx, hiddenAPISupportingModules) -} + // Generate the monotlithic hiddenapi-flags.csv file. + allFlags := hiddenAPISingletonPaths(ctx).flags + buildRuleToGenerateHiddenApiFlags(ctx, "hiddenAPIFlagsFile", "hiddenapi flags", allFlags, stubFlags, annotationFlags, monolithicInfo.FlagsFilesByCategory, monolithicInfo.AllFlagsPaths, android.OptionalPath{}) -func (b *platformBootclasspathModule) generateHiddenAPIStubFlagsRules(ctx android.ModuleContext, modules []hiddenAPISupportingModule) { - bootDexJars := android.Paths{} - for _, module := range modules { - bootDexJars = append(bootDexJars, module.bootDexJar) - } + // Generate an intermediate monolithic hiddenapi-metadata.csv file directly from the annotations + // in the source code. + intermediateMetadataCSV := android.PathForModuleOut(ctx, "hiddenapi-monolithic", "intermediate-metadata.csv") + buildRuleToGenerateMetadata(ctx, "monolithic hidden API metadata", classesJars, stubFlags, intermediateMetadataCSV) - sdkKindToStubPaths := hiddenAPIGatherStubLibDexJarPaths(ctx) + // Reformat the intermediate file to add | quotes just in case that is important for the tools + // that consume the metadata file. + // TODO(b/179354495): Investigate whether it is possible to remove this reformatting step. + metadataCSV := hiddenAPISingletonPaths(ctx).metadata + b.buildRuleMergeCSV(ctx, "reformat monolithic hidden API metadata", android.Paths{intermediateMetadataCSV}, metadataCSV) - outputPath := hiddenAPISingletonPaths(ctx).stubFlags - rule := ruleToGenerateHiddenAPIStubFlagsFile(ctx, outputPath, bootDexJars, sdkKindToStubPaths) - rule.Build("platform-bootclasspath-monolithic-hiddenapi-stub-flags", "monolithic hidden API stub flags") + // Generate the monolithic hiddenapi-index.csv file directly from the CSV files in the classes + // jars. + indexCSV := hiddenAPISingletonPaths(ctx).index + buildRuleToGenerateIndex(ctx, "monolithic hidden API index", classesJars, indexCSV) } -func (b *platformBootclasspathModule) generateHiddenAPIIndexRules(ctx android.ModuleContext, modules []hiddenAPISupportingModule) { - indexes := android.Paths{} - for _, module := range modules { - indexes = append(indexes, module.indexCSV) - } - - rule := android.NewRuleBuilder(pctx, ctx) - rule.Command(). - BuiltTool("merge_csv"). - Flag("--key_field signature"). - FlagWithArg("--header=", "signature,file,startline,startcol,endline,endcol,properties"). - FlagWithOutput("--output=", hiddenAPISingletonPaths(ctx).index). - Inputs(indexes) - rule.Build("platform-bootclasspath-monolithic-hiddenapi-index", "monolithic hidden API index") -} +// createAndProvideMonolithicHiddenAPIInfo creates a MonolithicHiddenAPIInfo and provides it for +// testing. +func (b *platformBootclasspathModule) createAndProvideMonolithicHiddenAPIInfo(ctx android.ModuleContext, fragments []android.Module) MonolithicHiddenAPIInfo { + // Create a temporary input structure in which to collate information provided directly by this + // module, either through properties or direct dependencies. + temporaryInput := newHiddenAPIFlagInput() -func (b *platformBootclasspathModule) generatedHiddenAPIMetadataRules(ctx android.ModuleContext, modules []hiddenAPISupportingModule) { - metadataCSVFiles := android.Paths{} - for _, module := range modules { - metadataCSVFiles = append(metadataCSVFiles, module.metadataCSV) - } + // Create paths to the flag files specified in the properties. + temporaryInput.extractFlagFilesFromProperties(ctx, &b.properties.Hidden_api) - rule := android.NewRuleBuilder(pctx, ctx) + // Create the monolithic info, by starting with the flag files specified on this and then merging + // in information from all the fragment dependencies of this. + monolithicInfo := newMonolithicHiddenAPIInfo(ctx, temporaryInput.FlagFilesByCategory, fragments) - outputPath := hiddenAPISingletonPaths(ctx).metadata + // Store the information for testing. + ctx.SetProvider(monolithicHiddenAPIInfoProvider, monolithicInfo) + return monolithicInfo +} +func (b *platformBootclasspathModule) buildRuleMergeCSV(ctx android.ModuleContext, desc string, inputPaths android.Paths, outputPath android.WritablePath) { + rule := android.NewRuleBuilder(pctx, ctx) rule.Command(). BuiltTool("merge_csv"). Flag("--key_field signature"). FlagWithOutput("--output=", outputPath). - Inputs(metadataCSVFiles) + Inputs(inputPaths) - rule.Build("platform-bootclasspath-monolithic-hiddenapi-metadata", "monolithic hidden API metadata") + rule.Build(desc, desc) } // generateHiddenApiMakeVars generates make variables needed by hidden API related make rules, e.g. @@ -430,7 +368,7 @@ func (b *platformBootclasspathModule) generateHiddenApiMakeVars(ctx android.Make } // generateBootImageBuildActions generates ninja rules related to the boot image creation. -func (b *platformBootclasspathModule) generateBootImageBuildActions(ctx android.ModuleContext, updatableModules []android.Module) { +func (b *platformBootclasspathModule) generateBootImageBuildActions(ctx android.ModuleContext, nonUpdatableModules, updatableModules []android.Module) { // Force the GlobalSoongConfig to be created and cached for use by the dex_bootjars // GenerateSingletonBuildActions method as it cannot create it for itself. dexpreopt.GetGlobalSoongConfig(ctx) @@ -451,5 +389,16 @@ func (b *platformBootclasspathModule) generateBootImageBuildActions(ctx android. // Generate the updatable bootclasspath packages rule. generateUpdatableBcpPackagesRule(ctx, imageConfig, updatableModules) + // Copy non-updatable module dex jars to their predefined locations. + copyBootJarsToPredefinedLocations(ctx, nonUpdatableModules, imageConfig.modules, imageConfig.dexPaths) + + // Copy updatable module dex jars to their predefined locations. + config := GetUpdatableBootConfig(ctx) + copyBootJarsToPredefinedLocations(ctx, updatableModules, config.modules, config.dexPaths) + + // Build a profile for the image config and then use that to build the boot image. + profile := bootImageProfileRule(ctx, imageConfig) + buildBootImage(ctx, imageConfig, profile) + dumpOatRules(ctx, imageConfig) } diff --git a/java/platform_bootclasspath_test.go b/java/platform_bootclasspath_test.go index 98d46143c..d332f63df 100644 --- a/java/platform_bootclasspath_test.go +++ b/java/platform_bootclasspath_test.go @@ -155,6 +155,8 @@ func TestPlatformBootclasspath(t *testing.T) { func TestPlatformBootclasspath_Fragments(t *testing.T) { result := android.GroupFixturePreparers( prepareForTestWithPlatformBootclasspath, + PrepareForTestWithJavaSdkLibraryFiles, + FixtureWithLastReleaseApis("foo"), android.FixtureWithRootAndroidBp(` platform_bootclasspath { name: "platform-bootclasspath", @@ -192,6 +194,9 @@ func TestPlatformBootclasspath_Fragments(t *testing.T) { bootclasspath_fragment { name: "bar-fragment", contents: ["bar"], + api: { + stub_libs: ["foo"], + }, hidden_api: { unsupported: [ "bar-unsupported.txt", @@ -227,19 +232,34 @@ func TestPlatformBootclasspath_Fragments(t *testing.T) { sdk_version: "none", compile_dex: true, } + + java_sdk_library { + name: "foo", + srcs: ["a.java"], + public: { + enabled: true, + }, + compile_dex: true, + } `), ).RunTest(t) pbcp := result.Module("platform-bootclasspath", "android_common") - info := result.ModuleProvider(pbcp, hiddenAPIFlagFileInfoProvider).(hiddenAPIFlagFileInfo) + info := result.ModuleProvider(pbcp, monolithicHiddenAPIInfoProvider).(MonolithicHiddenAPIInfo) for _, category := range hiddenAPIFlagFileCategories { name := category.propertyName message := fmt.Sprintf("category %s", name) filename := strings.ReplaceAll(name, "_", "-") expected := []string{fmt.Sprintf("%s.txt", filename), fmt.Sprintf("bar-%s.txt", filename)} - android.AssertPathsRelativeToTopEquals(t, message, expected, info.categoryToPaths[category]) + android.AssertPathsRelativeToTopEquals(t, message, expected, info.FlagsFilesByCategory[category]) } + + android.AssertPathsRelativeToTopEquals(t, "stub flags", []string{"out/soong/.intermediates/bar-fragment/android_common/modular-hiddenapi/stub-flags.csv"}, info.StubFlagsPaths) + android.AssertPathsRelativeToTopEquals(t, "annotation flags", []string{"out/soong/.intermediates/bar-fragment/android_common/modular-hiddenapi/annotation-flags.csv"}, info.AnnotationFlagsPaths) + android.AssertPathsRelativeToTopEquals(t, "metadata flags", []string{"out/soong/.intermediates/bar-fragment/android_common/modular-hiddenapi/metadata.csv"}, info.MetadataPaths) + android.AssertPathsRelativeToTopEquals(t, "index flags", []string{"out/soong/.intermediates/bar-fragment/android_common/modular-hiddenapi/index.csv"}, info.IndexPaths) + android.AssertPathsRelativeToTopEquals(t, "all flags", []string{"out/soong/.intermediates/bar-fragment/android_common/modular-hiddenapi/all-flags.csv"}, info.AllFlagsPaths) } func TestPlatformBootclasspathVariant(t *testing.T) { @@ -407,20 +427,14 @@ func TestPlatformBootclasspath_HiddenAPIMonolithicFiles(t *testing.T) { } `) - platformBootclasspath := result.ModuleForTests("myplatform-bootclasspath", "android_common") - indexRule := platformBootclasspath.Rule("platform-bootclasspath-monolithic-hiddenapi-index") - CheckHiddenAPIRuleInputs(t, ` -.intermediates/bar/android_common/hiddenapi/index.csv -.intermediates/foo/android_common/hiddenapi/index.csv -`, - indexRule) - // Make sure that the foo-hiddenapi-annotations.jar is included in the inputs to the rules that // creates the index.csv file. - foo := result.ModuleForTests("foo", "android_common") - indexParams := foo.Output("hiddenapi/index.csv") + platformBootclasspath := result.ModuleForTests("myplatform-bootclasspath", "android_common") + indexRule := platformBootclasspath.Rule("monolithic_hidden_API_index") CheckHiddenAPIRuleInputs(t, ` +.intermediates/bar/android_common/javac/bar.jar .intermediates/foo-hiddenapi-annotations/android_common/javac/foo-hiddenapi-annotations.jar .intermediates/foo/android_common/javac/foo.jar -`, indexParams) +`, + indexRule) } diff --git a/java/sdk_library.go b/java/sdk_library.go index 800e93b20..8f36758c8 100644 --- a/java/sdk_library.go +++ b/java/sdk_library.go @@ -845,19 +845,7 @@ func (c *commonToSdkLibraryAndImport) selectHeaderJarsForSdkVersion(ctx android. // closest kind which is a subset of the requested kind. e.g. if requesting android.SdkModule then // it will return *scopePaths for android.SdkSystem if available or android.SdkPublic of not. func (c *commonToSdkLibraryAndImport) selectScopePaths(ctx android.BaseModuleContext, kind android.SdkKind) *scopePaths { - var apiScope *apiScope - switch kind { - case android.SdkSystem: - apiScope = apiScopeSystem - case android.SdkModule: - apiScope = apiScopeModuleLib - case android.SdkTest: - apiScope = apiScopeTest - case android.SdkSystemServer: - apiScope = apiScopeSystemServer - default: - apiScope = apiScopePublic - } + apiScope := sdkKindToApiScope(kind) paths := c.findClosestScopePath(apiScope) if paths == nil { @@ -874,6 +862,24 @@ func (c *commonToSdkLibraryAndImport) selectScopePaths(ctx android.BaseModuleCon return paths } +// sdkKindToApiScope maps from android.SdkKind to apiScope. +func sdkKindToApiScope(kind android.SdkKind) *apiScope { + var apiScope *apiScope + switch kind { + case android.SdkSystem: + apiScope = apiScopeSystem + case android.SdkModule: + apiScope = apiScopeModuleLib + case android.SdkTest: + apiScope = apiScopeTest + case android.SdkSystemServer: + apiScope = apiScopeSystemServer + default: + apiScope = apiScopePublic + } + return apiScope +} + // to satisfy SdkLibraryDependency interface func (c *commonToSdkLibraryAndImport) SdkApiStubDexJar(ctx android.BaseModuleContext, kind android.SdkKind) android.Path { paths := c.selectScopePaths(ctx, kind) @@ -884,6 +890,17 @@ func (c *commonToSdkLibraryAndImport) SdkApiStubDexJar(ctx android.BaseModuleCon return paths.stubsDexJarPath } +// to satisfy SdkLibraryDependency interface +func (c *commonToSdkLibraryAndImport) SdkRemovedTxtFile(ctx android.BaseModuleContext, kind android.SdkKind) android.OptionalPath { + apiScope := sdkKindToApiScope(kind) + paths := c.findScopePaths(apiScope) + if paths == nil { + return android.OptionalPath{} + } + + return paths.removedApiFilePath +} + func (c *commonToSdkLibraryAndImport) sdkComponentPropertiesForChildLibrary() interface{} { componentProps := &struct { SdkLibraryToImplicitlyTrack *string @@ -900,11 +917,17 @@ func (c *commonToSdkLibraryAndImport) sdkComponentPropertiesForChildLibrary() in return componentProps } -// Check if this can be used as a shared library. func (c *commonToSdkLibraryAndImport) sharedLibrary() bool { return proptools.BoolDefault(c.commonSdkLibraryProperties.Shared_library, true) } +// Check if the stub libraries should be compiled for dex +func (c *commonToSdkLibraryAndImport) stubLibrariesCompiledForDex() bool { + // Always compile the dex file files for the stub libraries if they will be used on the + // bootclasspath. + return !c.sharedLibrary() +} + // Properties related to the use of a module as an component of a java_sdk_library. type SdkLibraryComponentProperties struct { @@ -958,7 +981,7 @@ var _ SdkLibraryComponentDependency = (*Import)(nil) var _ SdkLibraryComponentDependency = (*SdkLibrary)(nil) var _ SdkLibraryComponentDependency = (*SdkLibraryImport)(nil) -// Provides access to sdk_version related header and implentation jars. +// Provides access to sdk_version related files, e.g. header and implementation jars. type SdkLibraryDependency interface { SdkLibraryComponentDependency @@ -978,6 +1001,12 @@ type SdkLibraryDependency interface { // SdkApiStubDexJar returns the dex jar for the stubs. It is needed by the hiddenapi processing // tool which processes dex files. SdkApiStubDexJar(ctx android.BaseModuleContext, kind android.SdkKind) android.Path + + // SdkRemovedTxtFile returns the optional path to the removed.txt file for the specified sdk kind. + SdkRemovedTxtFile(ctx android.BaseModuleContext, kind android.SdkKind) android.OptionalPath + + // sharedLibrary returns true if this can be used as a shared library. + sharedLibrary() bool } type SdkLibrary struct { @@ -1226,16 +1255,13 @@ func childModuleVisibility(childVisibility []string) []string { // Creates the implementation java library func (module *SdkLibrary) createImplLibrary(mctx android.DefaultableHookContext) { - moduleNamePtr := proptools.StringPtr(module.BaseModuleName()) - visibility := childModuleVisibility(module.sdkLibraryProperties.Impl_library_visibility) props := struct { - Name *string - Visibility []string - Instrument bool - Libs []string - ConfigurationName *string + Name *string + Visibility []string + Instrument bool + Libs []string }{ Name: proptools.StringPtr(module.implLibraryModuleName()), Visibility: visibility, @@ -1244,9 +1270,6 @@ func (module *SdkLibrary) createImplLibrary(mctx android.DefaultableHookContext) // Set the impl_only libs. Note that the module's "Libs" get appended as well, via the // addition of &module.properties below. Libs: module.sdkLibraryProperties.Impl_only_libs, - - // Make the created library behave as if it had the same name as this module. - ConfigurationName: moduleNamePtr, } properties := []interface{}{ @@ -1309,9 +1332,13 @@ func (module *SdkLibrary) createStubsLibrary(mctx android.DefaultableHookContext // We compile the stubs for 1.8 in line with the main android.jar stubs, and potential // interop with older developer tools that don't support 1.9. props.Java_version = proptools.StringPtr("1.8") - if module.dexProperties.Compile_dex != nil { - props.Compile_dex = module.dexProperties.Compile_dex + + // The imports need to be compiled to dex if the java_sdk_library requests it. + compileDex := module.dexProperties.Compile_dex + if module.stubLibrariesCompiledForDex() { + compileDex = proptools.BoolPtr(true) } + props.Compile_dex = compileDex // Dist the class jar artifact for sdk builds. if !Bool(module.sdkLibraryProperties.No_dist) { @@ -1539,7 +1566,7 @@ func PrebuiltJars(ctx android.BaseModuleContext, baseName string, s android.SdkS func withinSameApexesAs(ctx android.BaseModuleContext, other android.Module) bool { apexInfo := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo) otherApexInfo := ctx.OtherModuleProvider(other, android.ApexInfoProvider).(android.ApexInfo) - return len(otherApexInfo.InApexes) > 0 && reflect.DeepEqual(apexInfo.InApexes, otherApexInfo.InApexes) + return len(otherApexInfo.InApexVariants) > 0 && reflect.DeepEqual(apexInfo.InApexVariants, otherApexInfo.InApexVariants) } func (module *SdkLibrary) sdkJars(ctx android.BaseModuleContext, sdkVersion android.SdkSpec, headerJars bool) android.Paths { @@ -1622,8 +1649,12 @@ func (module *SdkLibrary) CreateInternalModules(mctx android.DefaultableHookCont path := path.Join(mctx.ModuleDir(), apiDir, scope.apiFilePrefix+api) p := android.ExistentPathForSource(mctx, path) if !p.Valid() { - mctx.ModuleErrorf("Current api file %#v doesn't exist", path) - missingCurrentApi = true + if mctx.Config().AllowMissingDependencies() { + mctx.AddMissingDependencies([]string{path}) + } else { + mctx.ModuleErrorf("Current api file %#v doesn't exist", path) + missingCurrentApi = true + } } } } @@ -1969,7 +2000,11 @@ func (module *SdkLibraryImport) createJavaImportForStubs(mctx android.Defaultabl props.Prefer = proptools.BoolPtr(module.prebuilt.Prefer()) // The imports need to be compiled to dex if the java_sdk_library_import requests it. - props.Compile_dex = module.properties.Compile_dex + compileDex := module.properties.Compile_dex + if module.stubLibrariesCompiledForDex() { + compileDex = proptools.BoolPtr(true) + } + props.Compile_dex = compileDex mctx.CreateModule(ImportFactory, &props, module.sdkComponentPropertiesForChildLibrary()) } @@ -2110,8 +2145,7 @@ func (module *SdkLibraryImport) GenerateAndroidBuildActions(ctx android.ModuleCo di := ctx.OtherModuleProvider(deapexerModule, android.DeapexerProvider).(android.DeapexerInfo) if dexOutputPath := di.PrebuiltExportPath(module.BaseModuleName(), ".dexjar"); dexOutputPath != nil { module.dexJarFile = dexOutputPath - module.initHiddenAPI(ctx, module.configurationName) - module.hiddenAPIExtractInformation(ctx, dexOutputPath, module.findScopePaths(apiScopePublic).stubsImplPath[0]) + module.initHiddenAPI(ctx, dexOutputPath, module.findScopePaths(apiScopePublic).stubsImplPath[0], nil) } else { // This should never happen as a variant for a prebuilt_apex is only created if the // prebuilt_apex has been configured to export the java library dex file. @@ -2482,11 +2516,18 @@ func (s *sdkLibrarySdkMemberProperties) AddToPropertySet(ctx android.SdkMemberCo } scopeSet.AddProperty("jars", jars) - // Merge the stubs source jar into the snapshot zip so that when it is unpacked - // the source files are also unpacked. - snapshotRelativeDir := filepath.Join(scopeDir, ctx.Name()+"_stub_sources") - ctx.SnapshotBuilder().UnzipToSnapshot(properties.StubsSrcJar, snapshotRelativeDir) - scopeSet.AddProperty("stub_srcs", []string{snapshotRelativeDir}) + if ctx.SdkModuleContext().Config().IsEnvTrue("SOONG_SDK_SNAPSHOT_USE_SRCJAR") { + // Copy the stubs source jar into the snapshot zip as is. + srcJarSnapshotPath := filepath.Join(scopeDir, ctx.Name()+".srcjar") + ctx.SnapshotBuilder().CopyToSnapshot(properties.StubsSrcJar, srcJarSnapshotPath) + scopeSet.AddProperty("stub_srcs", []string{srcJarSnapshotPath}) + } else { + // Merge the stubs source jar into the snapshot zip so that when it is unpacked + // the source files are also unpacked. + snapshotRelativeDir := filepath.Join(scopeDir, ctx.Name()+"_stub_sources") + ctx.SnapshotBuilder().UnzipToSnapshot(properties.StubsSrcJar, snapshotRelativeDir) + scopeSet.AddProperty("stub_srcs", []string{snapshotRelativeDir}) + } if properties.CurrentApiFile != nil { currentApiSnapshotPath := filepath.Join(scopeDir, ctx.Name()+".txt") diff --git a/java/systemserver_classpath_fragment.go b/java/systemserver_classpath_fragment.go index 82cdb8926..a72b3f60c 100644 --- a/java/systemserver_classpath_fragment.go +++ b/java/systemserver_classpath_fragment.go @@ -17,6 +17,7 @@ package java import ( "android/soong/android" "android/soong/dexpreopt" + "github.com/google/blueprint" ) func init() { @@ -24,8 +25,8 @@ func init() { } func registerSystemserverClasspathBuildComponents(ctx android.RegistrationContext) { - // TODO(satayev): add systemserver_classpath_fragment module ctx.RegisterModuleType("platform_systemserverclasspath", platformSystemServerClasspathFactory) + ctx.RegisterModuleType("systemserverclasspath_fragment", systemServerClasspathFactory) } type platformSystemServerClasspathModule struct { @@ -41,27 +42,84 @@ func platformSystemServerClasspathFactory() android.Module { return m } -func (b *platformSystemServerClasspathModule) AndroidMkEntries() (entries []android.AndroidMkEntries) { - return b.classpathFragmentBase().androidMkEntries() +func (p *platformSystemServerClasspathModule) AndroidMkEntries() (entries []android.AndroidMkEntries) { + return p.classpathFragmentBase().androidMkEntries() } -func (b *platformSystemServerClasspathModule) GenerateAndroidBuildActions(ctx android.ModuleContext) { - configuredJars := configuredJarListToClasspathJars(ctx, b.ClasspathFragmentToConfiguredJarList(ctx), b.classpathType) - b.classpathFragmentBase().generateClasspathProtoBuildActions(ctx, configuredJars) +func (p *platformSystemServerClasspathModule) GenerateAndroidBuildActions(ctx android.ModuleContext) { + classpathJars := configuredJarListToClasspathJars(ctx, p.ClasspathFragmentToConfiguredJarList(ctx), p.classpathType) + p.classpathFragmentBase().generateClasspathProtoBuildActions(ctx, classpathJars) } -var platformSystemServerClasspathKey = android.NewOnceKey("platform_systemserverclasspath") +func (p *platformSystemServerClasspathModule) ClasspathFragmentToConfiguredJarList(ctx android.ModuleContext) android.ConfiguredJarList { + global := dexpreopt.GetGlobalConfig(ctx) -func (b *platformSystemServerClasspathModule) ClasspathFragmentToConfiguredJarList(ctx android.ModuleContext) android.ConfiguredJarList { - return ctx.Config().Once(platformSystemServerClasspathKey, func() interface{} { - global := dexpreopt.GetGlobalConfig(ctx) + jars := global.SystemServerJars + // TODO(satayev): split apex jars into separate configs. + for i := 0; i < global.UpdatableSystemServerJars.Len(); i++ { + jars = jars.Append(global.UpdatableSystemServerJars.Apex(i), global.UpdatableSystemServerJars.Jar(i)) + } + return jars +} + +type SystemServerClasspathModule struct { + android.ModuleBase + android.ApexModuleBase + + ClasspathFragmentBase + + properties systemServerClasspathFragmentProperties +} + +func (s *SystemServerClasspathModule) ShouldSupportSdkVersion(ctx android.BaseModuleContext, sdkVersion android.ApiLevel) error { + return nil +} + +type systemServerClasspathFragmentProperties struct { + // The contents of this systemserverclasspath_fragment, could be either java_library, or java_sdk_library. + // + // The order of this list matters as it is the order that is used in the SYSTEMSERVERCLASSPATH. + Contents []string +} + +func systemServerClasspathFactory() android.Module { + m := &SystemServerClasspathModule{} + m.AddProperties(&m.properties) + android.InitApexModule(m) + initClasspathFragment(m, SYSTEMSERVERCLASSPATH) + android.InitAndroidArchModule(m, android.DeviceSupported, android.MultilibCommon) + return m +} + +func (s *SystemServerClasspathModule) GenerateAndroidBuildActions(ctx android.ModuleContext) { + if len(s.properties.Contents) == 0 { + ctx.PropertyErrorf("contents", "empty contents are not allowed") + } + + classpathJars := configuredJarListToClasspathJars(ctx, s.ClasspathFragmentToConfiguredJarList(ctx), s.classpathType) + s.classpathFragmentBase().generateClasspathProtoBuildActions(ctx, classpathJars) +} + +func (s *SystemServerClasspathModule) ClasspathFragmentToConfiguredJarList(ctx android.ModuleContext) android.ConfiguredJarList { + // TODO(satayev): populate with actual content + return android.EmptyConfiguredJarList() +} + +type systemServerClasspathFragmentContentDependencyTag struct { + blueprint.BaseDependencyTag +} + +// The tag used for the dependency between the systemserverclasspath_fragment module and its contents. +var systemServerClasspathFragmentContentDepTag = systemServerClasspathFragmentContentDependencyTag{} + +func IsSystemServerClasspathFragmentContentDepTag(tag blueprint.DependencyTag) bool { + return tag == systemServerClasspathFragmentContentDepTag +} - jars := global.SystemServerJars +func (s *SystemServerClasspathModule) ComponentDepsMutator(ctx android.BottomUpMutatorContext) { + module := ctx.Module() - // TODO(satayev): split apex jars into separate configs. - for i := 0; i < global.UpdatableSystemServerJars.Len(); i++ { - jars = jars.Append(global.UpdatableSystemServerJars.Apex(i), global.UpdatableSystemServerJars.Jar(i)) - } - return jars - }).(android.ConfiguredJarList) + for _, name := range s.properties.Contents { + ctx.AddDependency(module, systemServerClasspathFragmentContentDepTag, name) + } } diff --git a/java/systemserver_classpath_fragment_test.go b/java/systemserver_classpath_fragment_test.go index 6126d5eb1..5272f271d 100644 --- a/java/systemserver_classpath_fragment_test.go +++ b/java/systemserver_classpath_fragment_test.go @@ -20,13 +20,13 @@ import ( "android/soong/android" ) -var prepareForTestWithSystemserverClasspath = android.GroupFixturePreparers( +var prepareForTestWithSystemServerClasspath = android.GroupFixturePreparers( PrepareForTestWithJavaDefaultModules, ) -func TestSystemserverClasspathVariant(t *testing.T) { +func TestPlatformSystemserverClasspathVariant(t *testing.T) { result := android.GroupFixturePreparers( - prepareForTestWithSystemserverClasspath, + prepareForTestWithSystemServerClasspath, android.FixtureWithRootAndroidBp(` platform_systemserverclasspath { name: "platform-systemserverclasspath", @@ -38,9 +38,9 @@ func TestSystemserverClasspathVariant(t *testing.T) { android.AssertIntEquals(t, "expect 1 variant", 1, len(variants)) } -func TestSystemserverClasspath_ClasspathFragmentPaths(t *testing.T) { +func TestPlatformSystemserverClasspath_ClasspathFragmentPaths(t *testing.T) { result := android.GroupFixturePreparers( - prepareForTestWithSystemserverClasspath, + prepareForTestWithSystemServerClasspath, android.FixtureWithRootAndroidBp(` platform_systemserverclasspath { name: "platform-systemserverclasspath", @@ -53,9 +53,9 @@ func TestSystemserverClasspath_ClasspathFragmentPaths(t *testing.T) { android.AssertPathRelativeToTopEquals(t, "install filepath", "out/soong/target/product/test_device/system/etc/classpaths", p.ClasspathFragmentBase.installDirPath) } -func TestSystemserverClasspathModule_AndroidMkEntries(t *testing.T) { +func TestPlatformSystemserverClasspathModule_AndroidMkEntries(t *testing.T) { preparer := android.GroupFixturePreparers( - prepareForTestWithSystemserverClasspath, + prepareForTestWithSystemServerClasspath, android.FixtureWithRootAndroidBp(` platform_systemserverclasspath { name: "platform-systemserverclasspath", @@ -95,3 +95,14 @@ func TestSystemserverClasspathModule_AndroidMkEntries(t *testing.T) { } }) } + +func TestSystemserverclasspathFragmentWithoutContents(t *testing.T) { + prepareForTestWithSystemServerClasspath. + ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern( + `\Qempty contents are not allowed\E`)). + RunTestWithBp(t, ` + systemserverclasspath_fragment { + name: "systemserverclasspath-fragment", + } + `) +} diff --git a/java/testing.go b/java/testing.go index 387d59573..1fef337cc 100644 --- a/java/testing.go +++ b/java/testing.go @@ -24,6 +24,7 @@ import ( "android/soong/android" "android/soong/cc" "android/soong/dexpreopt" + "github.com/google/blueprint" ) @@ -55,8 +56,9 @@ var PrepareForTestWithJavaBuildComponents = android.GroupFixturePreparers( }.AddToFixture(), ) -// Test fixture preparer that will define default java modules, e.g. standard prebuilt modules. -var PrepareForTestWithJavaDefaultModules = android.GroupFixturePreparers( +// Test fixture preparer that will define all default java modules except the +// fake_tool_binary for dex2oatd. +var PrepareForTestWithJavaDefaultModulesWithoutFakeDex2oatd = android.GroupFixturePreparers( // Make sure that all the module types used in the defaults are registered. PrepareForTestWithJavaBuildComponents, // Additional files needed when test disallows non-existent source. @@ -72,6 +74,11 @@ var PrepareForTestWithJavaDefaultModules = android.GroupFixturePreparers( android.FixtureAddTextFile(defaultJavaDir+"/Android.bp", gatherRequiredDepsForTest()), // Add dexpreopt compat libs (android.test.base, etc.) and a fake dex2oatd module. dexpreopt.PrepareForTestWithDexpreoptCompatLibs, +) + +// Test fixture preparer that will define default java modules, e.g. standard prebuilt modules. +var PrepareForTestWithJavaDefaultModules = android.GroupFixturePreparers( + PrepareForTestWithJavaDefaultModulesWithoutFakeDex2oatd, dexpreopt.PrepareForTestWithFakeDex2oatd, ) @@ -371,7 +378,7 @@ func apexNamePairFromModule(ctx *android.TestContext, module android.Module) str if apexInfo.IsForPlatform() { apex = "platform" } else { - apex = apexInfo.InApexes[0] + apex = apexInfo.InApexVariants[0] } return fmt.Sprintf("%s:%s", apex, name) diff --git a/rust/Android.bp b/rust/Android.bp index f45404ff2..b611672d4 100644 --- a/rust/Android.bp +++ b/rust/Android.bp @@ -31,8 +31,9 @@ bootstrap_go_package { "protobuf.go", "rust.go", "sanitize.go", - "strip.go", "source_provider.go", + "snapshot_utils.go", + "strip.go", "test.go", "testing.go", ], diff --git a/rust/compiler.go b/rust/compiler.go index a3f02c0d4..1598ebf9a 100644 --- a/rust/compiler.go +++ b/rust/compiler.go @@ -341,6 +341,11 @@ func (compiler *baseCompiler) crateName() string { return compiler.Properties.Crate_name } +func (compiler *baseCompiler) everInstallable() bool { + // Most modules are installable, so return true by default. + return true +} + func (compiler *baseCompiler) installDir(ctx ModuleContext) android.InstallPath { dir := compiler.dir if ctx.toolchain().Is64Bit() && compiler.dir64 != "" { diff --git a/rust/proc_macro.go b/rust/proc_macro.go index 4eead3267..c217959cd 100644 --- a/rust/proc_macro.go +++ b/rust/proc_macro.go @@ -82,3 +82,8 @@ func (procMacro *procMacroDecorator) getStem(ctx ModuleContext) string { func (procMacro *procMacroDecorator) autoDep(ctx android.BottomUpMutatorContext) autoDep { return rlibAutoDep } + +func (procMacro *procMacroDecorator) everInstallable() bool { + // Proc_macros are never installed + return false +} diff --git a/rust/rust.go b/rust/rust.go index bb971420e..46c8f250c 100644 --- a/rust/rust.go +++ b/rust/rust.go @@ -97,6 +97,7 @@ type BaseProperties struct { PreventInstall bool HideFromMake bool + Installable *bool } type Module struct { @@ -143,6 +144,10 @@ func (mod *Module) SetHideFromMake() { mod.Properties.HideFromMake = true } +func (c *Module) HiddenFromMake() bool { + return c.Properties.HideFromMake +} + func (mod *Module) SanitizePropDefined() bool { // Because compiler is not set for some Rust modules where sanitize might be set, check that compiler is also not // nil since we need compiler to actually sanitize. @@ -210,6 +215,38 @@ func (mod *Module) Shared() bool { return false } +func (mod *Module) Dylib() bool { + if mod.compiler != nil { + if library, ok := mod.compiler.(libraryInterface); ok { + return library.dylib() + } + } + return false +} + +func (mod *Module) Rlib() bool { + if mod.compiler != nil { + if library, ok := mod.compiler.(libraryInterface); ok { + return library.rlib() + } + } + return false +} + +func (mod *Module) Binary() bool { + if mod.compiler != nil { + if _, ok := mod.compiler.(*binaryDecorator); ok { + return true + } + } + return false +} + +func (mod *Module) Object() bool { + // Rust has no modules which produce only object files. + return false +} + func (mod *Module) Toc() android.OptionalPath { if mod.compiler != nil { if _, ok := mod.compiler.(libraryInterface); ok { @@ -223,12 +260,13 @@ func (mod *Module) UseSdk() bool { return false } -// Returns true if the module is using VNDK libraries instead of the libraries in /system/lib or /system/lib64. -// "product" and "vendor" variant modules return true for this function. -// When BOARD_VNDK_VERSION is set, vendor variants of "vendor_available: true", "vendor: true", -// "soc_specific: true" and more vendor installed modules are included here. -// When PRODUCT_PRODUCT_VNDK_VERSION is set, product variants of "vendor_available: true" or -// "product_specific: true" modules are included here. +func (mod *Module) RelativeInstallPath() string { + if mod.compiler != nil { + return mod.compiler.relativeInstallPath() + } + return "" +} + func (mod *Module) UseVndk() bool { return mod.Properties.VndkVersion != "" } @@ -250,6 +288,10 @@ func (mod *Module) IsVndkExt() bool { return false } +func (mod *Module) IsVndkSp() bool { + return false +} + func (c *Module) IsVndkPrivate() bool { return false } @@ -274,6 +316,14 @@ func (m *Module) NeedsVendorPublicLibraryVariants() bool { return false } +func (mod *Module) HasLlndkStubs() bool { + return false +} + +func (mod *Module) StubsVersion() string { + panic(fmt.Errorf("StubsVersion called on non-versioned module: %q", mod.BaseModuleName())) +} + func (mod *Module) SdkVersion() string { return "" } @@ -362,6 +412,7 @@ type compiler interface { inData() bool install(ctx ModuleContext) relativeInstallPath() string + everInstallable() bool nativeCoverage() bool @@ -423,8 +474,12 @@ func (mod *Module) IsNativeCoverageNeeded(ctx android.BaseModuleContext) bool { return mod.coverage != nil && mod.coverage.Properties.NeedCoverageVariant } -func (mod *Module) PreventInstall() { - mod.Properties.PreventInstall = true +func (mod *Module) VndkVersion() string { + return mod.Properties.VndkVersion +} + +func (mod *Module) PreventInstall() bool { + return mod.Properties.PreventInstall } func (mod *Module) HideFromMake() { @@ -564,6 +619,10 @@ func (mod *Module) CoverageFiles() android.Paths { } func (mod *Module) installable(apexInfo android.ApexInfo) bool { + if !mod.EverInstallable() { + return false + } + // The apex variant is not installable because it is included in the APEX and won't appear // in the system partition as a standalone file. if !apexInfo.IsForPlatform() { @@ -676,6 +735,16 @@ func (mod *Module) nativeCoverage() bool { return mod.compiler != nil && mod.compiler.nativeCoverage() } +func (mod *Module) EverInstallable() bool { + return mod.compiler != nil && + // Check to see whether the module is actually ever installable. + mod.compiler.everInstallable() +} + +func (mod *Module) Installable() *bool { + return mod.Properties.Installable +} + func (mod *Module) toolchain(ctx android.BaseModuleContext) config.Toolchain { if mod.cachedToolchain == nil { mod.cachedToolchain = config.FindToolchain(ctx.Os(), ctx.Arch()) diff --git a/rust/sanitize.go b/rust/sanitize.go index 0a53f989f..3d14d512f 100644 --- a/rust/sanitize.go +++ b/rust/sanitize.go @@ -189,6 +189,22 @@ func (sanitize *sanitize) SetSanitizer(t cc.SanitizerType, b bool) { } } +func (m *Module) UbsanRuntimeNeeded() bool { + return false +} + +func (m *Module) MinimalRuntimeNeeded() bool { + return false +} + +func (m *Module) UbsanRuntimeDep() bool { + return false +} + +func (m *Module) MinimalRuntimeDep() bool { + return false +} + // Check if the sanitizer is explicitly disabled (as opposed to nil by // virtue of not being set). func (sanitize *sanitize) isSanitizerExplicitlyDisabled(t cc.SanitizerType) bool { diff --git a/rust/snapshot_utils.go b/rust/snapshot_utils.go new file mode 100644 index 000000000..e0ed1f711 --- /dev/null +++ b/rust/snapshot_utils.go @@ -0,0 +1,54 @@ +// Copyright 2021 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package rust + +import ( + "android/soong/android" +) + +func (mod *Module) ExcludeFromVendorSnapshot() bool { + // TODO Rust does not yet support snapshotting + return true +} + +func (mod *Module) ExcludeFromRecoverySnapshot() bool { + // TODO Rust does not yet support snapshotting + return true +} + +func (mod *Module) IsSnapshotLibrary() bool { + // TODO Rust does not yet support snapshotting + return false +} + +func (mod *Module) SnapshotRuntimeLibs() []string { + // TODO Rust does not yet support a runtime libs notion similar to CC + return []string{} +} + +func (mod *Module) SnapshotSharedLibs() []string { + // TODO Rust does not yet support snapshotting + return []string{} +} + +func (mod *Module) Symlinks() []string { + // TODO update this to return the list of symlinks when Rust supports defining symlinks + return nil +} + +func (m *Module) SnapshotHeaders() android.Paths { + // TODO Rust does not yet support snapshotting + return android.Paths{} +} diff --git a/scripts/build-rustdocs.sh b/scripts/build-rustdocs.sh new file mode 100755 index 000000000..ad8ba16d5 --- /dev/null +++ b/scripts/build-rustdocs.sh @@ -0,0 +1,31 @@ +#!/bin/bash -ex + +# Copyright 2021 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. + +# Builds the platform rustdocs and copies them to the dist directory to provide +# online docs for each build. + +if [ -z "${OUT_DIR}" ]; then + echo Must set OUT_DIR + exit 1 +fi + +source build/envsetup.sh +m rustdoc + +if [ -n "${DIST_DIR}" ]; then + mkdir -p ${DIST_DIR} + cp -r ${OUT_DIR}/soong/rustdoc $DIST_DIR/rustdoc +fi diff --git a/scripts/hiddenapi/Android.bp b/scripts/hiddenapi/Android.bp index af7e7fe6b..7472f528b 100644 --- a/scripts/hiddenapi/Android.bp +++ b/scripts/hiddenapi/Android.bp @@ -47,3 +47,18 @@ python_binary_host { }, }, } + +python_binary_host { + name: "verify_overlaps", + main: "verify_overlaps.py", + srcs: ["verify_overlaps.py"], + version: { + py2: { + enabled: false, + }, + py3: { + enabled: true, + embedded_launcher: true, + }, + }, +} diff --git a/scripts/hiddenapi/verify_overlaps.py b/scripts/hiddenapi/verify_overlaps.py new file mode 100755 index 000000000..c8e387931 --- /dev/null +++ b/scripts/hiddenapi/verify_overlaps.py @@ -0,0 +1,69 @@ +#!/usr/bin/env python +# +# Copyright (C) 2018 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +Verify that one set of hidden API flags is a subset of another. +""" + +import argparse +import csv + +args_parser = argparse.ArgumentParser(description='Verify that one set of hidden API flags is a subset of another.') +args_parser.add_argument('all', help='All the flags') +args_parser.add_argument('subsets', nargs=argparse.REMAINDER, help='Subsets of the flags') +args = args_parser.parse_args() + + +def dict_reader(input): + return csv.DictReader(input, delimiter=',', quotechar='|', fieldnames=['signature']) + +# Read in all the flags into a dict indexed by signature +allFlagsBySignature = {} +with open(args.all, 'r') as allFlagsFile: + allFlagsReader = dict_reader(allFlagsFile) + for row in allFlagsReader: + signature = row['signature'] + allFlagsBySignature[signature]=row + +failed = False +for subsetPath in args.subsets: + mismatchingSignatures = [] + with open(subsetPath, 'r') as subsetFlagsFile: + subsetReader = dict_reader(subsetFlagsFile) + for row in subsetReader: + signature = row['signature'] + if signature in allFlagsBySignature: + allFlags = allFlagsBySignature.get(signature) + if allFlags != row: + mismatchingSignatures.append((signature, row[None], allFlags[None])) + else: + mismatchingSignatures.append((signature, row[None], [])) + + + if mismatchingSignatures: + failed = True + print("ERROR: Hidden API flags are inconsistent:") + print("< " + subsetPath) + print("> " + args.all) + for mismatch in mismatchingSignatures: + print() + print("< " + mismatch[0] + "," + ",".join(mismatch[1])) + if mismatch[2] != None: + print("> " + mismatch[0] + "," + ",".join(mismatch[2])) + else: + print("> " + mismatch[0] + " - missing") + +if failed: + sys.exit(1) diff --git a/sdk/bootclasspath_fragment_sdk_test.go b/sdk/bootclasspath_fragment_sdk_test.go index e91546e0b..bd69f06f2 100644 --- a/sdk/bootclasspath_fragment_sdk_test.go +++ b/sdk/bootclasspath_fragment_sdk_test.go @@ -165,21 +165,33 @@ func TestSnapshotWithBootClasspathFragment_Contents(t *testing.T) { prepareForSdkTestWithJava, java.PrepareForTestWithJavaDefaultModules, java.PrepareForTestWithJavaSdkLibraryFiles, - java.FixtureWithLastReleaseApis("mysdklibrary", "mycoreplatform"), + java.FixtureWithLastReleaseApis("mysdklibrary", "myothersdklibrary", "mycoreplatform"), android.FixtureWithRootAndroidBp(` sdk { name: "mysdk", bootclasspath_fragments: ["mybootclasspathfragment"], - java_sdk_libs: ["mysdklibrary", "mycoreplatform"], + java_sdk_libs: [ + // This is not strictly needed as it should be automatically added to the sdk_snapshot as + // a java_sdk_libs module because it is used in the mybootclasspathfragment's + // api.stub_libs property. However, it is specified here to ensure that duplicates are + // correctly deduped. + "mysdklibrary", + ], } bootclasspath_fragment { name: "mybootclasspathfragment", - contents: ["mybootlib"], + contents: [ + // This should be automatically added to the sdk_snapshot as a java_boot_libs module. + "mybootlib", + // This should be automatically added to the sdk_snapshot as a java_sdk_libs module. + "myothersdklibrary", + ], api: { stub_libs: ["mysdklibrary"], }, core_platform_api: { + // This should be automatically added to the sdk_snapshot as a java_sdk_libs module. stub_libs: ["mycoreplatform"], }, } @@ -195,14 +207,21 @@ func TestSnapshotWithBootClasspathFragment_Contents(t *testing.T) { java_sdk_library { name: "mysdklibrary", srcs: ["Test.java"], - compile_dex: true, + shared_library: false, + public: {enabled: true}, + } + + java_sdk_library { + name: "myothersdklibrary", + srcs: ["Test.java"], + shared_library: false, public: {enabled: true}, } java_sdk_library { name: "mycoreplatform", srcs: ["Test.java"], - compile_dex: true, + shared_library: false, public: {enabled: true}, } `), @@ -217,13 +236,23 @@ prebuilt_bootclasspath_fragment { prefer: false, visibility: ["//visibility:public"], apex_available: ["//apex_available:platform"], - contents: ["mybootlib"], + contents: [ + "mybootlib", + "myothersdklibrary", + ], api: { stub_libs: ["mysdklibrary"], }, core_platform_api: { stub_libs: ["mycoreplatform"], }, + hidden_api: { + stub_flags: "hiddenapi/stub-flags.csv", + annotation_flags: "hiddenapi/annotation-flags.csv", + metadata: "hiddenapi/metadata.csv", + index: "hiddenapi/index.csv", + all_flags: "hiddenapi/all-flags.csv", + }, } java_import { @@ -235,12 +264,26 @@ java_import { } java_sdk_library_import { + name: "myothersdklibrary", + prefer: false, + visibility: ["//visibility:public"], + apex_available: ["//apex_available:platform"], + shared_library: false, + public: { + jars: ["sdk_library/public/myothersdklibrary-stubs.jar"], + stub_srcs: ["sdk_library/public/myothersdklibrary_stub_sources"], + current_api: "sdk_library/public/myothersdklibrary.txt", + removed_api: "sdk_library/public/myothersdklibrary-removed.txt", + sdk_version: "current", + }, +} + +java_sdk_library_import { name: "mysdklibrary", prefer: false, visibility: ["//visibility:public"], apex_available: ["//apex_available:platform"], - shared_library: true, - compile_dex: true, + shared_library: false, public: { jars: ["sdk_library/public/mysdklibrary-stubs.jar"], stub_srcs: ["sdk_library/public/mysdklibrary_stub_sources"], @@ -255,8 +298,7 @@ java_sdk_library_import { prefer: false, visibility: ["//visibility:public"], apex_available: ["//apex_available:platform"], - shared_library: true, - compile_dex: true, + shared_library: false, public: { jars: ["sdk_library/public/mycoreplatform-stubs.jar"], stub_srcs: ["sdk_library/public/mycoreplatform_stub_sources"], @@ -265,7 +307,7 @@ java_sdk_library_import { sdk_version: "current", }, } -`), + `), checkVersionedAndroidBpContents(` // This is auto-generated. DO NOT EDIT. @@ -274,13 +316,23 @@ prebuilt_bootclasspath_fragment { sdk_member_name: "mybootclasspathfragment", visibility: ["//visibility:public"], apex_available: ["//apex_available:platform"], - contents: ["mysdk_mybootlib@current"], + contents: [ + "mysdk_mybootlib@current", + "mysdk_myothersdklibrary@current", + ], api: { stub_libs: ["mysdk_mysdklibrary@current"], }, core_platform_api: { stub_libs: ["mysdk_mycoreplatform@current"], }, + hidden_api: { + stub_flags: "hiddenapi/stub-flags.csv", + annotation_flags: "hiddenapi/annotation-flags.csv", + metadata: "hiddenapi/metadata.csv", + index: "hiddenapi/index.csv", + all_flags: "hiddenapi/all-flags.csv", + }, } java_import { @@ -292,12 +344,26 @@ java_import { } java_sdk_library_import { + name: "mysdk_myothersdklibrary@current", + sdk_member_name: "myothersdklibrary", + visibility: ["//visibility:public"], + apex_available: ["//apex_available:platform"], + shared_library: false, + public: { + jars: ["sdk_library/public/myothersdklibrary-stubs.jar"], + stub_srcs: ["sdk_library/public/myothersdklibrary_stub_sources"], + current_api: "sdk_library/public/myothersdklibrary.txt", + removed_api: "sdk_library/public/myothersdklibrary-removed.txt", + sdk_version: "current", + }, +} + +java_sdk_library_import { name: "mysdk_mysdklibrary@current", sdk_member_name: "mysdklibrary", visibility: ["//visibility:public"], apex_available: ["//apex_available:platform"], - shared_library: true, - compile_dex: true, + shared_library: false, public: { jars: ["sdk_library/public/mysdklibrary-stubs.jar"], stub_srcs: ["sdk_library/public/mysdklibrary_stub_sources"], @@ -312,8 +378,7 @@ java_sdk_library_import { sdk_member_name: "mycoreplatform", visibility: ["//visibility:public"], apex_available: ["//apex_available:platform"], - shared_library: true, - compile_dex: true, + shared_library: false, public: { jars: ["sdk_library/public/mycoreplatform-stubs.jar"], stub_srcs: ["sdk_library/public/mycoreplatform_stub_sources"], @@ -329,13 +394,22 @@ sdk_snapshot { bootclasspath_fragments: ["mysdk_mybootclasspathfragment@current"], java_boot_libs: ["mysdk_mybootlib@current"], java_sdk_libs: [ + "mysdk_myothersdklibrary@current", "mysdk_mysdklibrary@current", "mysdk_mycoreplatform@current", ], } -`), + `), checkAllCopyRules(` +.intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/stub-flags.csv -> hiddenapi/stub-flags.csv +.intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/annotation-flags.csv -> hiddenapi/annotation-flags.csv +.intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/metadata.csv -> hiddenapi/metadata.csv +.intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/index.csv -> hiddenapi/index.csv +.intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/all-flags.csv -> hiddenapi/all-flags.csv .intermediates/mybootlib/android_common/javac/mybootlib.jar -> java/mybootlib.jar +.intermediates/myothersdklibrary.stubs/android_common/javac/myothersdklibrary.stubs.jar -> sdk_library/public/myothersdklibrary-stubs.jar +.intermediates/myothersdklibrary.stubs.source/android_common/metalava/myothersdklibrary.stubs.source_api.txt -> sdk_library/public/myothersdklibrary.txt +.intermediates/myothersdklibrary.stubs.source/android_common/metalava/myothersdklibrary.stubs.source_removed.txt -> sdk_library/public/myothersdklibrary-removed.txt .intermediates/mysdklibrary.stubs/android_common/javac/mysdklibrary.stubs.jar -> sdk_library/public/mysdklibrary-stubs.jar .intermediates/mysdklibrary.stubs.source/android_common/metalava/mysdklibrary.stubs.source_api.txt -> sdk_library/public/mysdklibrary.txt .intermediates/mysdklibrary.stubs.source/android_common/metalava/mysdklibrary.stubs.source_removed.txt -> sdk_library/public/mysdklibrary-removed.txt @@ -384,6 +458,10 @@ func TestBasicSdkWithBootclasspathFragment(t *testing.T) { func TestSnapshotWithBootclasspathFragment_HiddenAPI(t *testing.T) { result := android.GroupFixturePreparers( prepareForSdkTestWithJava, + java.PrepareForTestWithJavaDefaultModules, + java.PrepareForTestWithJavaSdkLibraryFiles, + java.FixtureWithLastReleaseApis("mysdklibrary"), + prepareForSdkTestWithApex, android.MockFS{ "my-blocked.txt": nil, "my-max-target-o-low-priority.txt": nil, @@ -400,9 +478,20 @@ func TestSnapshotWithBootclasspathFragment_HiddenAPI(t *testing.T) { bootclasspath_fragments: ["mybootclasspathfragment"], } + apex { + name: "myapex", + key: "myapex.key", + min_sdk_version: "1", + bootclasspath_fragments: ["mybootclasspathfragment"], + } + bootclasspath_fragment { name: "mybootclasspathfragment", + apex_available: ["myapex"], contents: ["mybootlib"], + api: { + stub_libs: ["mysdklibrary"], + }, hidden_api: { unsupported: [ "my-unsupported.txt", @@ -433,11 +522,20 @@ func TestSnapshotWithBootclasspathFragment_HiddenAPI(t *testing.T) { java_library { name: "mybootlib", + apex_available: ["myapex"], srcs: ["Test.java"], system_modules: "none", sdk_version: "none", + min_sdk_version: "1", compile_dex: true, } + + java_sdk_library { + name: "mysdklibrary", + srcs: ["Test.java"], + compile_dex: true, + public: {enabled: true}, + } `), ).RunTest(t) @@ -449,8 +547,11 @@ prebuilt_bootclasspath_fragment { name: "mybootclasspathfragment", prefer: false, visibility: ["//visibility:public"], - apex_available: ["//apex_available:platform"], + apex_available: ["myapex"], contents: ["mybootlib"], + api: { + stub_libs: ["mysdklibrary"], + }, hidden_api: { unsupported: ["hiddenapi/my-unsupported.txt"], removed: ["hiddenapi/my-removed.txt"], @@ -460,6 +561,11 @@ prebuilt_bootclasspath_fragment { max_target_o_low_priority: ["hiddenapi/my-max-target-o-low-priority.txt"], blocked: ["hiddenapi/my-blocked.txt"], unsupported_packages: ["hiddenapi/my-unsupported-packages.txt"], + stub_flags: "hiddenapi/stub-flags.csv", + annotation_flags: "hiddenapi/annotation-flags.csv", + metadata: "hiddenapi/metadata.csv", + index: "hiddenapi/index.csv", + all_flags: "hiddenapi/all-flags.csv", }, } @@ -467,9 +573,25 @@ java_import { name: "mybootlib", prefer: false, visibility: ["//visibility:public"], - apex_available: ["//apex_available:platform"], + apex_available: ["myapex"], jars: ["java/mybootlib.jar"], } + +java_sdk_library_import { + name: "mysdklibrary", + prefer: false, + visibility: ["//visibility:public"], + apex_available: ["//apex_available:platform"], + shared_library: true, + compile_dex: true, + public: { + jars: ["sdk_library/public/mysdklibrary-stubs.jar"], + stub_srcs: ["sdk_library/public/mysdklibrary_stub_sources"], + current_api: "sdk_library/public/mysdklibrary.txt", + removed_api: "sdk_library/public/mysdklibrary-removed.txt", + sdk_version: "current", + }, +} `), checkAllCopyRules(` my-unsupported.txt -> hiddenapi/my-unsupported.txt @@ -480,7 +602,15 @@ my-max-target-p.txt -> hiddenapi/my-max-target-p.txt my-max-target-o-low-priority.txt -> hiddenapi/my-max-target-o-low-priority.txt my-blocked.txt -> hiddenapi/my-blocked.txt my-unsupported-packages.txt -> hiddenapi/my-unsupported-packages.txt +.intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/stub-flags.csv -> hiddenapi/stub-flags.csv +.intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/annotation-flags.csv -> hiddenapi/annotation-flags.csv +.intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/metadata.csv -> hiddenapi/metadata.csv +.intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/index.csv -> hiddenapi/index.csv +.intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/all-flags.csv -> hiddenapi/all-flags.csv .intermediates/mybootlib/android_common/javac/mybootlib.jar -> java/mybootlib.jar +.intermediates/mysdklibrary.stubs/android_common/javac/mysdklibrary.stubs.jar -> sdk_library/public/mysdklibrary-stubs.jar +.intermediates/mysdklibrary.stubs.source/android_common/metalava/mysdklibrary.stubs.source_api.txt -> sdk_library/public/mysdklibrary.txt +.intermediates/mysdklibrary.stubs.source/android_common/metalava/mysdklibrary.stubs.source_removed.txt -> sdk_library/public/mysdklibrary-removed.txt `), ) } diff --git a/sdk/java_sdk_test.go b/sdk/java_sdk_test.go index dc58d9309..6f769a3f1 100644 --- a/sdk/java_sdk_test.go +++ b/sdk/java_sdk_test.go @@ -1089,6 +1089,57 @@ sdk_snapshot { ) } +func TestSnapshotWithJavaSdkLibrary_UseSrcJar(t *testing.T) { + result := android.GroupFixturePreparers( + prepareForSdkTestWithJavaSdkLibrary, + android.FixtureMergeEnv(map[string]string{ + "SOONG_SDK_SNAPSHOT_USE_SRCJAR": "true", + }), + ).RunTestWithBp(t, ` + sdk { + name: "mysdk", + java_sdk_libs: ["myjavalib"], + } + + java_sdk_library { + name: "myjavalib", + srcs: ["Test.java"], + sdk_version: "current", + shared_library: false, + public: { + enabled: true, + }, + } + `) + + CheckSnapshot(t, result, "mysdk", "", + checkUnversionedAndroidBpContents(` +// This is auto-generated. DO NOT EDIT. + +java_sdk_library_import { + name: "myjavalib", + prefer: false, + visibility: ["//visibility:public"], + apex_available: ["//apex_available:platform"], + shared_library: false, + public: { + jars: ["sdk_library/public/myjavalib-stubs.jar"], + stub_srcs: ["sdk_library/public/myjavalib.srcjar"], + current_api: "sdk_library/public/myjavalib.txt", + removed_api: "sdk_library/public/myjavalib-removed.txt", + sdk_version: "current", + }, +} + `), + checkAllCopyRules(` +.intermediates/myjavalib.stubs/android_common/javac/myjavalib.stubs.jar -> sdk_library/public/myjavalib-stubs.jar +.intermediates/myjavalib.stubs.source/android_common/metalava/myjavalib.stubs.source-stubs.srcjar -> sdk_library/public/myjavalib.srcjar +.intermediates/myjavalib.stubs.source/android_common/metalava/myjavalib.stubs.source_api.txt -> sdk_library/public/myjavalib.txt +.intermediates/myjavalib.stubs.source/android_common/metalava/myjavalib.stubs.source_removed.txt -> sdk_library/public/myjavalib-removed.txt + `), + ) +} + func TestSnapshotWithJavaSdkLibrary_CompileDex(t *testing.T) { result := android.GroupFixturePreparers(prepareForSdkTestWithJavaSdkLibrary).RunTestWithBp(t, ` sdk { diff --git a/tests/bootstrap_test.sh b/tests/bootstrap_test.sh index 3f51114f9..42363e9c0 100755 --- a/tests/bootstrap_test.sh +++ b/tests/bootstrap_test.sh @@ -7,6 +7,8 @@ set -o pipefail source "$(dirname "$0")/lib.sh" +readonly GENERATED_BUILD_FILE_NAME="BUILD.bazel" + function test_smoke { setup run_soong @@ -505,8 +507,8 @@ filegroup { EOF GENERATE_BAZEL_FILES=1 run_soong - [[ -e out/soong/bp2build/a/BUILD ]] || fail "a/BUILD not created" - [[ -L out/soong/workspace/a/BUILD ]] || fail "a/BUILD not symlinked" + [[ -e out/soong/bp2build/a/${GENERATED_BUILD_FILE_NAME} ]] || fail "a/${GENERATED_BUILD_FILE_NAME} not created" + [[ -L out/soong/workspace/a/${GENERATED_BUILD_FILE_NAME} ]] || fail "a/${GENERATED_BUILD_FILE_NAME} not symlinked" mkdir -p b touch b/b.txt @@ -519,8 +521,8 @@ filegroup { EOF GENERATE_BAZEL_FILES=1 run_soong - [[ -e out/soong/bp2build/b/BUILD ]] || fail "a/BUILD not created" - [[ -L out/soong/workspace/b/BUILD ]] || fail "a/BUILD not symlinked" + [[ -e out/soong/bp2build/b/${GENERATED_BUILD_FILE_NAME} ]] || fail "a/${GENERATED_BUILD_FILE_NAME} not created" + [[ -L out/soong/workspace/b/${GENERATED_BUILD_FILE_NAME} ]] || fail "a/${GENERATED_BUILD_FILE_NAME} not symlinked" } function test_bp2build_null_build { @@ -551,11 +553,11 @@ filegroup { EOF GENERATE_BAZEL_FILES=1 run_soong - grep -q a1.txt out/soong/bp2build/a/BUILD || fail "a1.txt not in BUILD file" + grep -q a1.txt "out/soong/bp2build/a/${GENERATED_BUILD_FILE_NAME}" || fail "a1.txt not in ${GENERATED_BUILD_FILE_NAME} file" touch a/a2.txt GENERATE_BAZEL_FILES=1 run_soong - grep -q a2.txt out/soong/bp2build/a/BUILD || fail "a2.txt not in BUILD file" + grep -q a2.txt "out/soong/bp2build/a/${GENERATED_BUILD_FILE_NAME}" || fail "a2.txt not in ${GENERATED_BUILD_FILE_NAME} file" } function test_dump_json_module_graph() { @@ -583,8 +585,8 @@ EOF GENERATE_BAZEL_FILES=1 run_soong [[ -e out/soong/workspace ]] || fail "Bazel workspace not created" [[ -d out/soong/workspace/a/b ]] || fail "module directory not a directory" - [[ -L out/soong/workspace/a/b/BUILD ]] || fail "BUILD file not symlinked" - [[ "$(readlink -f out/soong/workspace/a/b/BUILD)" =~ bp2build/a/b/BUILD$ ]] \ + [[ -L "out/soong/workspace/a/b/${GENERATED_BUILD_FILE_NAME}" ]] || fail "${GENERATED_BUILD_FILE_NAME} file not symlinked" + [[ "$(readlink -f out/soong/workspace/a/b/${GENERATED_BUILD_FILE_NAME})" =~ "bp2build/a/b/${GENERATED_BUILD_FILE_NAME}"$ ]] \ || fail "BUILD files symlinked at the wrong place" [[ -L out/soong/workspace/a/b/b.txt ]] || fail "a/b/b.txt not symlinked" [[ -L out/soong/workspace/a/a.txt ]] || fail "a/b/a.txt not symlinked" @@ -616,7 +618,7 @@ function test_bp2build_build_file_precedence { mkdir -p a touch a/a.txt - touch a/BUILD + touch a/${GENERATED_BUILD_FILE_NAME} cat > a/Android.bp <<EOF filegroup { name: "a", @@ -626,15 +628,15 @@ filegroup { EOF GENERATE_BAZEL_FILES=1 run_soong - [[ -L out/soong/workspace/a/BUILD ]] || fail "BUILD file not symlinked" - [[ "$(readlink -f out/soong/workspace/a/BUILD)" =~ bp2build/a/BUILD$ ]] \ - || fail "BUILD files symlinked to the wrong place" + [[ -L "out/soong/workspace/a/${GENERATED_BUILD_FILE_NAME}" ]] || fail "${GENERATED_BUILD_FILE_NAME} file not symlinked" + [[ "$(readlink -f out/soong/workspace/a/${GENERATED_BUILD_FILE_NAME})" =~ "bp2build/a/${GENERATED_BUILD_FILE_NAME}"$ ]] \ + || fail "${GENERATED_BUILD_FILE_NAME} files symlinked to the wrong place" } function test_bp2build_reports_multiple_errors { setup - mkdir -p a/BUILD + mkdir -p "a/${GENERATED_BUILD_FILE_NAME}" touch a/a.txt cat > a/Android.bp <<EOF filegroup { @@ -644,7 +646,7 @@ filegroup { } EOF - mkdir -p b/BUILD + mkdir -p "b/${GENERATED_BUILD_FILE_NAME}" touch b/b.txt cat > b/Android.bp <<EOF filegroup { @@ -658,8 +660,8 @@ EOF fail "Build should have failed" fi - grep -q "a/BUILD' exist" "$MOCK_TOP/errors" || fail "Error for a/BUILD not found" - grep -q "b/BUILD' exist" "$MOCK_TOP/errors" || fail "Error for b/BUILD not found" + grep -q "a/${GENERATED_BUILD_FILE_NAME}' exist" "$MOCK_TOP/errors" || fail "Error for a/${GENERATED_BUILD_FILE_NAME} not found" + grep -q "b/${GENERATED_BUILD_FILE_NAME}' exist" "$MOCK_TOP/errors" || fail "Error for b/${GENERATED_BUILD_FILE_NAME} not found" } test_smoke diff --git a/tests/bp2build_bazel_test.sh b/tests/bp2build_bazel_test.sh index 082cd0671..e3577107f 100755 --- a/tests/bp2build_bazel_test.sh +++ b/tests/bp2build_bazel_test.sh @@ -6,6 +6,8 @@ set -o pipefail source "$(dirname "$0")/lib.sh" +readonly GENERATED_BUILD_FILE_NAME="BUILD.bazel" + function test_bp2build_generates_all_buildfiles { setup create_mock_bazel @@ -40,24 +42,24 @@ EOF run_bp2build - if [[ ! -f "./out/soong/workspace/foo/convertible_soong_module/BUILD" ]]; then - fail "./out/soong/workspace/foo/convertible_soong_module/BUILD was not generated" + if [[ ! -f "./out/soong/workspace/foo/convertible_soong_module/${GENERATED_BUILD_FILE_NAME}" ]]; then + fail "./out/soong/workspace/foo/convertible_soong_module/${GENERATED_BUILD_FILE_NAME} was not generated" fi - if [[ ! -f "./out/soong/workspace/foo/unconvertible_soong_module/BUILD" ]]; then - fail "./out/soong/workspace/foo/unconvertible_soong_module/BUILD was not generated" + if [[ ! -f "./out/soong/workspace/foo/unconvertible_soong_module/${GENERATED_BUILD_FILE_NAME}" ]]; then + fail "./out/soong/workspace/foo/unconvertible_soong_module/${GENERATED_BUILD_FILE_NAME} was not generated" fi - if ! grep "the_answer" "./out/soong/workspace/foo/convertible_soong_module/BUILD"; then - fail "missing BUILD target the_answer in convertible_soong_module/BUILD" + if ! grep "the_answer" "./out/soong/workspace/foo/convertible_soong_module/${GENERATED_BUILD_FILE_NAME}"; then + fail "missing BUILD target the_answer in convertible_soong_module/${GENERATED_BUILD_FILE_NAME}" fi - if grep "not_the_answer" "./out/soong/workspace/foo/unconvertible_soong_module/BUILD"; then - fail "found unexpected BUILD target not_the_answer in unconvertible_soong_module/BUILD" + if grep "not_the_answer" "./out/soong/workspace/foo/unconvertible_soong_module/${GENERATED_BUILD_FILE_NAME}"; then + fail "found unexpected BUILD target not_the_answer in unconvertible_soong_module/${GENERATED_BUILD_FILE_NAME}" fi - if ! grep "filegroup" "./out/soong/workspace/foo/unconvertible_soong_module/BUILD"; then - fail "missing filegroup in unconvertible_soong_module/BUILD" + if ! grep "filegroup" "./out/soong/workspace/foo/unconvertible_soong_module/${GENERATED_BUILD_FILE_NAME}"; then + fail "missing filegroup in unconvertible_soong_module/${GENERATED_BUILD_FILE_NAME}" fi # NOTE: We don't actually use the extra BUILD file for anything here diff --git a/tests/lib.sh b/tests/lib.sh index e561a3d49..35ccea973 100644 --- a/tests/lib.sh +++ b/tests/lib.sh @@ -114,6 +114,7 @@ function create_mock_bazel() { symlink_directory prebuilts/jdk symlink_file WORKSPACE + symlink_file BUILD symlink_file tools/bazel } diff --git a/ui/build/finder.go b/ui/build/finder.go index 2eb84ca38..09d53cc1e 100644 --- a/ui/build/finder.go +++ b/ui/build/finder.go @@ -76,6 +76,8 @@ func NewSourceFinder(ctx Context, config Config) (f *finder.Finder) { "Blueprints", // Bazel build definitions. "BUILD.bazel", + // Bazel build definitions. + "BUILD", // Kati clean definitions. "CleanSpec.mk", // Ownership definition. @@ -102,7 +104,7 @@ func NewSourceFinder(ctx Context, config Config) (f *finder.Finder) { func findBazelFiles(entries finder.DirEntries) (dirNames []string, fileNames []string) { matches := []string{} for _, foundName := range entries.FileNames { - if foundName == "BUILD.bazel" || foundName == "WORKSPACE" || strings.HasSuffix(foundName, ".bzl") { + if foundName == "BUILD.bazel" || foundName == "BUILD" || foundName == "WORKSPACE" || strings.HasSuffix(foundName, ".bzl") { matches = append(matches, foundName) } } diff --git a/zip/zip.go b/zip/zip.go index 84e974bce..6e412c956 100644 --- a/zip/zip.go +++ b/zip/zip.go @@ -656,9 +656,11 @@ func (z *ZipWriter) addFile(dest, src string, method uint16, emulateJar, srcJar UncompressedSize64: uint64(fileSize), } + mode := os.FileMode(0600) if executable { - header.SetMode(0700) + mode = 0700 } + header.SetMode(mode) err = createParentDirs(dest, src) if err != nil { diff --git a/zip/zip_test.go b/zip/zip_test.go index a37ae41e4..441dea3bd 100644 --- a/zip/zip_test.go +++ b/zip/zip_test.go @@ -62,7 +62,7 @@ func fh(name string, contents []byte, method uint16) zip.FileHeader { Method: method, CRC32: crc32.ChecksumIEEE(contents), UncompressedSize64: uint64(len(contents)), - ExternalAttrs: 0, + ExternalAttrs: (syscall.S_IFREG | 0600) << 16, } } |