diff --git a/Android.bp b/Android.bp index 0ca11d338..6910d7587 100644 --- a/Android.bp +++ b/Android.bp @@ -503,6 +503,7 @@ bootstrap_go_package { "soong-java", ], srcs: [ + "sdk/bp.go", "sdk/sdk.go", "sdk/update.go", ], diff --git a/android/sdk.go b/android/sdk.go index 73cb2565f..01e18ed4b 100644 --- a/android/sdk.go +++ b/android/sdk.go @@ -167,16 +167,39 @@ type SnapshotBuilder interface { // Unzip the supplied zip into the snapshot relative directory destDir. UnzipToSnapshot(zipPath Path, destDir string) - // Get the AndroidBpFile for the snapshot. - AndroidBpFile() GeneratedSnapshotFile - - // Get a versioned name appropriate for the SDK snapshot version being taken. - VersionedSdkMemberName(unversionedName string) interface{} + // Add a new prebuilt module to the snapshot. The returned module + // must be populated with the module type specific properties. The following + // properties will be automatically populated. + // + // * name + // * sdk_member_name + // * prefer + // + // This will result in two Soong modules being generated in the Android. One + // that is versioned, coupled to the snapshot version and marked as + // prefer=true. And one that is not versioned, not marked as prefer=true and + // will only be used if the equivalently named non-prebuilt module is not + // present. + AddPrebuiltModule(name string, moduleType string) BpModule } -// Provides support for generating a file, e.g. the Android.bp file. -type GeneratedSnapshotFile interface { - Printfln(format string, args ...interface{}) - Indent() - Dedent() +// A set of properties for use in a .bp file. +type BpPropertySet interface { + // Add a property, the value can be one of the following types: + // * string + // * array of the above + // * bool + // * BpPropertySet + // + // It is an error is multiples properties with the same name are added. + AddProperty(name string, value interface{}) + + // Add a property set with the specified name and return so that additional + // properties can be added. + AddPropertySet(name string) BpPropertySet +} + +// A .bp module definition. +type BpModule interface { + BpPropertySet } diff --git a/java/droiddoc.go b/java/droiddoc.go index 96c841638..16e6921e1 100644 --- a/java/droiddoc.go +++ b/java/droiddoc.go @@ -1979,28 +1979,6 @@ func (d *Droidstubs) BuildSnapshot(sdkModuleContext android.ModuleContext, build snapshotRelativeDir := filepath.Join("java", d.Name()+"_stubs_sources") builder.UnzipToSnapshot(stubsSrcJar, snapshotRelativeDir) - d.generatePrebuiltStubsSources(builder, snapshotRelativeDir, true) - - // This module is for the case when the source tree for the unversioned module - // doesn't exist (i.e. building in an unbundled tree). "prefer:" is set to false - // so that this module does not eclipse the unversioned module if it exists. - d.generatePrebuiltStubsSources(builder, snapshotRelativeDir, false) -} - -func (d *Droidstubs) generatePrebuiltStubsSources(builder android.SnapshotBuilder, snapshotRelativeDir string, versioned bool) { - bp := builder.AndroidBpFile() - name := d.Name() - bp.Printfln("prebuilt_stubs_sources {") - bp.Indent() - if versioned { - bp.Printfln("name: %q,", builder.VersionedSdkMemberName(name)) - bp.Printfln("sdk_member_name: %q,", name) - } else { - bp.Printfln("name: %q,", name) - bp.Printfln("prefer: false,") - } - bp.Printfln("srcs: [%q],", snapshotRelativeDir) - bp.Dedent() - bp.Printfln("}") - bp.Printfln("") + pbm := builder.AddPrebuiltModule(sdkModuleContext.OtherModuleName(d), "prebuilt_stubs_sources") + pbm.AddProperty("srcs", []string{snapshotRelativeDir}) } diff --git a/java/java.go b/java/java.go index 5cd074ab1..9c0fcbaa0 100644 --- a/java/java.go +++ b/java/java.go @@ -1737,30 +1737,8 @@ func (j *Library) BuildSnapshot(sdkModuleContext android.ModuleContext, builder } } - j.generateJavaImport(builder, snapshotRelativeJavaLibPath, true) - - // This module is for the case when the source tree for the unversioned module - // doesn't exist (i.e. building in an unbundled tree). "prefer:" is set to false - // so that this module does not eclipse the unversioned module if it exists. - j.generateJavaImport(builder, snapshotRelativeJavaLibPath, false) -} - -func (j *Library) generateJavaImport(builder android.SnapshotBuilder, snapshotRelativeJavaLibPath string, versioned bool) { - bp := builder.AndroidBpFile() - name := j.Name() - bp.Printfln("java_import {") - bp.Indent() - if versioned { - bp.Printfln("name: %q,", builder.VersionedSdkMemberName(name)) - bp.Printfln("sdk_member_name: %q,", name) - } else { - bp.Printfln("name: %q,", name) - bp.Printfln("prefer: false,") - } - bp.Printfln("jars: [%q],", snapshotRelativeJavaLibPath) - bp.Dedent() - bp.Printfln("}") - bp.Printfln("") + module := builder.AddPrebuiltModule(sdkModuleContext.OtherModuleName(j), "java_import") + module.AddProperty("jars", []string{snapshotRelativeJavaLibPath}) } // java_library builds and links sources into a `.jar` file for the device, and possibly for the host as well. diff --git a/sdk/bp.go b/sdk/bp.go new file mode 100644 index 000000000..19fb70dab --- /dev/null +++ b/sdk/bp.go @@ -0,0 +1,141 @@ +// Copyright (C) 2019 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 sdk + +import ( + "fmt" + + "android/soong/android" +) + +type bpPropertySet struct { + properties map[string]interface{} + order []string +} + +var _ android.BpPropertySet = (*bpPropertySet)(nil) + +func (s *bpPropertySet) init() { + s.properties = make(map[string]interface{}) +} + +func (s *bpPropertySet) AddProperty(name string, value interface{}) { + if s.properties[name] != nil { + panic("Property %q already exists in property set") + } + + s.properties[name] = value + s.order = append(s.order, name) +} + +func (s *bpPropertySet) AddPropertySet(name string) android.BpPropertySet { + set := &bpPropertySet{} + set.init() + s.AddProperty(name, set) + return set +} + +func (s *bpPropertySet) getValue(name string) interface{} { + return s.properties[name] +} + +func (s *bpPropertySet) copy() bpPropertySet { + propertiesCopy := make(map[string]interface{}) + for p, v := range s.properties { + propertiesCopy[p] = v + } + + return bpPropertySet{ + properties: propertiesCopy, + order: append([]string(nil), s.order...), + } +} + +func (s *bpPropertySet) setProperty(name string, value interface{}) { + if s.properties[name] == nil { + s.AddProperty(name, value) + } else { + s.properties[name] = value + } +} + +func (s *bpPropertySet) insertAfter(position string, name string, value interface{}) { + if s.properties[name] != nil { + panic("Property %q already exists in property set") + } + + // Add the name to the end of the order, to ensure it has necessary capacity + // and to handle the case when the position does not exist. + s.order = append(s.order, name) + + // Search through the order for the item that matches supplied position. If + // found then insert the name of the new property after it. + for i, v := range s.order { + if v == position { + // Copy the items after the one where the new property should be inserted. + copy(s.order[i+2:], s.order[i+1:]) + // Insert the item in the list. + s.order[i+1] = name + } + } + + s.properties[name] = value +} + +type bpModule struct { + bpPropertySet + moduleType string +} + +var _ android.BpModule = (*bpModule)(nil) + +func (m *bpModule) copy() *bpModule { + return &bpModule{ + bpPropertySet: m.bpPropertySet.copy(), + moduleType: m.moduleType, + } +} + +// A .bp file +type bpFile struct { + modules map[string]*bpModule + order []*bpModule +} + +// Add a module. +// +// The module must have had its "name" property set to a string value that +// is unique within this file. +func (f *bpFile) AddModule(module android.BpModule) { + m := module.(*bpModule) + if name, ok := m.getValue("name").(string); ok { + if f.modules[name] != nil { + panic(fmt.Sprintf("Module %q already exists in bp file", name)) + } + + f.modules[name] = m + f.order = append(f.order, m) + } else { + panic("Module does not have a name property, or it is not a string") + } +} + +func (f *bpFile) newModule(moduleType string) *bpModule { + module := &bpModule{ + moduleType: moduleType, + } + (&module.bpPropertySet).init() + return module +} diff --git a/sdk/sdk_test.go b/sdk/sdk_test.go index 1bbd28618..8def1d850 100644 --- a/sdk/sdk_test.go +++ b/sdk/sdk_test.go @@ -544,15 +544,9 @@ cc_prebuilt_library_shared { sdk_snapshot { name: "mysdk@current", - java_libs: [ - "mysdk_myjavalib@current", - ], - stubs_sources: [ - "mysdk_myjavaapistubs@current", - ], - native_shared_libs: [ - "mysdk_mynativelib@current", - ], + java_libs: ["mysdk_myjavalib@current"], + stubs_sources: ["mysdk_myjavaapistubs@current"], + native_shared_libs: ["mysdk_mynativelib@current"], } `) diff --git a/sdk/update.go b/sdk/update.go index 000d200ca..3bd63369d 100644 --- a/sdk/update.go +++ b/sdk/update.go @@ -17,6 +17,7 @@ package sdk import ( "fmt" "path/filepath" + "reflect" "strings" "github.com/google/blueprint/proptools" @@ -28,34 +29,37 @@ import ( var pctx = android.NewPackageContext("android/soong/sdk") -// generatedFile abstracts operations for writing contents into a file and emit a build rule -// for the file. -type generatedFile struct { - path android.OutputPath +type generatedContents struct { content strings.Builder indentLevel int } +// generatedFile abstracts operations for writing contents into a file and emit a build rule +// for the file. +type generatedFile struct { + generatedContents + path android.OutputPath +} + func newGeneratedFile(ctx android.ModuleContext, path ...string) *generatedFile { return &generatedFile{ - path: android.PathForModuleOut(ctx, path...).OutputPath, - indentLevel: 0, + path: android.PathForModuleOut(ctx, path...).OutputPath, } } -func (gf *generatedFile) Indent() { - gf.indentLevel++ +func (gc *generatedContents) Indent() { + gc.indentLevel++ } -func (gf *generatedFile) Dedent() { - gf.indentLevel-- +func (gc *generatedContents) Dedent() { + gc.indentLevel-- } -func (gf *generatedFile) Printfln(format string, args ...interface{}) { +func (gc *generatedContents) Printfln(format string, args ...interface{}) { // ninja consumes newline characters in rspfile_content. Prevent it by // escaping the backslash in the newline character. The extra backslash // is removed when the rspfile is written to the actual script file - fmt.Fprintf(&(gf.content), strings.Repeat(" ", gf.indentLevel)+format+"\\n", args...) + fmt.Fprintf(&(gc.content), strings.Repeat(" ", gc.indentLevel)+format+"\\n", args...) } func (gf *generatedFile) build(pctx android.PackageContext, ctx android.BuilderContext, implicits android.Paths) { @@ -239,15 +243,18 @@ func (s *sdk) buildSnapshot(ctx android.ModuleContext) android.OutputPath { snapshotDir := android.PathForModuleOut(ctx, "snapshot") bp := newGeneratedFile(ctx, "snapshot", "Android.bp") - bp.Printfln("// This is auto-generated. DO NOT EDIT.") - bp.Printfln("") + + bpFile := &bpFile{ + modules: make(map[string]*bpModule), + } builder := &snapshotBuilder{ - ctx: ctx, - version: "current", - snapshotDir: snapshotDir.OutputPath, - filesToZip: []android.Path{bp.path}, - androidBpFile: bp, + ctx: ctx, + version: "current", + snapshotDir: snapshotDir.OutputPath, + filesToZip: []android.Path{bp.path}, + bpFile: bpFile, + prebuiltModules: make(map[string]*bpModule), } s.builderForTests = builder @@ -269,41 +276,37 @@ func (s *sdk) buildSnapshot(ctx android.ModuleContext) android.OutputPath { buildSharedNativeLibSnapshot(ctx, info, builder) } - // generate Android.bp + for _, unversioned := range builder.prebuiltOrder { + // Copy the unversioned module so it can be modified to make it versioned. + versioned := unversioned.copy() + name := versioned.properties["name"].(string) + versioned.setProperty("name", builder.versionedSdkMemberName(name)) + versioned.insertAfter("name", "sdk_member_name", name) + bpFile.AddModule(versioned) - bp.Printfln("sdk_snapshot {") - bp.Indent() - bp.Printfln("name: %q,", ctx.ModuleName()+string(android.SdkVersionSeparator)+builder.version) + // Set prefer: false - this is not strictly required as that is the default. + unversioned.insertAfter("name", "prefer", false) + bpFile.AddModule(unversioned) + } + + // Create the snapshot module. + snapshotName := ctx.ModuleName() + string(android.SdkVersionSeparator) + builder.version + snapshotModule := bpFile.newModule("sdk_snapshot") + snapshotModule.AddProperty("name", snapshotName) if len(s.properties.Java_libs) > 0 { - bp.Printfln("java_libs: [") - bp.Indent() - for _, m := range s.properties.Java_libs { - bp.Printfln("%q,", builder.VersionedSdkMemberName(m)) - } - bp.Dedent() - bp.Printfln("],") // java_libs + snapshotModule.AddProperty("java_libs", builder.versionedSdkMemberNames(s.properties.Java_libs)) } if len(s.properties.Stubs_sources) > 0 { - bp.Printfln("stubs_sources: [") - bp.Indent() - for _, m := range s.properties.Stubs_sources { - bp.Printfln("%q,", builder.VersionedSdkMemberName(m)) - } - bp.Dedent() - bp.Printfln("],") // stubs_sources + snapshotModule.AddProperty("stubs_sources", builder.versionedSdkMemberNames(s.properties.Stubs_sources)) } if len(s.properties.Native_shared_libs) > 0 { - bp.Printfln("native_shared_libs: [") - bp.Indent() - for _, m := range s.properties.Native_shared_libs { - bp.Printfln("%q,", builder.VersionedSdkMemberName(m)) - } - bp.Dedent() - bp.Printfln("],") // native_shared_libs + snapshotModule.AddProperty("native_shared_libs", builder.versionedSdkMemberNames(s.properties.Native_shared_libs)) } - bp.Dedent() - bp.Printfln("}") // sdk_snapshot - bp.Printfln("") + bpFile.AddModule(snapshotModule) + + // generate Android.bp + bp = newGeneratedFile(ctx, "snapshot", "Android.bp") + generateBpContents(&bp.generatedContents, bpFile) bp.build(pctx, ctx, nil) @@ -351,8 +354,61 @@ func (s *sdk) buildSnapshot(ctx android.ModuleContext) android.OutputPath { return outputZipFile } +func generateBpContents(contents *generatedContents, bpFile *bpFile) { + contents.Printfln("// This is auto-generated. DO NOT EDIT.") + for _, bpModule := range bpFile.order { + contents.Printfln("") + contents.Printfln("%s {", bpModule.moduleType) + outputPropertySet(contents, &bpModule.bpPropertySet) + contents.Printfln("}") + } + contents.Printfln("") +} + +func outputPropertySet(contents *generatedContents, set *bpPropertySet) { + contents.Indent() + for _, name := range set.order { + value := set.properties[name] + + reflectedValue := reflect.ValueOf(value) + t := reflectedValue.Type() + + kind := t.Kind() + switch kind { + case reflect.Slice: + length := reflectedValue.Len() + if length > 1 { + contents.Printfln("%s: [", name) + contents.Indent() + for i := 0; i < length; i = i + 1 { + contents.Printfln("%q,", reflectedValue.Index(i).Interface()) + } + contents.Dedent() + contents.Printfln("],") + } else if length == 0 { + contents.Printfln("%s: [],", name) + } else { + contents.Printfln("%s: [%q],", name, reflectedValue.Index(0).Interface()) + } + case reflect.Bool: + contents.Printfln("%s: %t,", name, reflectedValue.Bool()) + + case reflect.Ptr: + contents.Printfln("%s: {", name) + outputPropertySet(contents, reflectedValue.Interface().(*bpPropertySet)) + contents.Printfln("},") + + default: + contents.Printfln("%s: %q,", name, value) + } + } + contents.Dedent() +} + func (s *sdk) GetAndroidBpContentsForTests() string { - return s.builderForTests.androidBpFile.content.String() + contents := &generatedContents{} + generateBpContents(contents, s.builderForTests.bpFile) + return contents.content.String() } func buildSharedNativeLibSnapshot(ctx android.ModuleContext, info *nativeLibInfo, builder android.SnapshotBuilder) { @@ -406,81 +462,57 @@ func buildSharedNativeLibSnapshot(ctx android.ModuleContext, info *nativeLibInfo } } - info.generatePrebuiltLibrary(ctx, builder, true) - - // This module is for the case when the source tree for the unversioned module - // doesn't exist (i.e. building in an unbundled tree). "prefer:" is set to false - // so that this module does not eclipse the unversioned module if it exists. - info.generatePrebuiltLibrary(ctx, builder, false) + info.generatePrebuiltLibrary(ctx, builder) } -func (info *nativeLibInfo) generatePrebuiltLibrary(ctx android.ModuleContext, builder android.SnapshotBuilder, versioned bool) { - bp := builder.AndroidBpFile() - bp.Printfln("cc_prebuilt_library_shared {") - bp.Indent() - name := info.name - if versioned { - bp.Printfln("name: %q,", builder.VersionedSdkMemberName(name)) - bp.Printfln("sdk_member_name: %q,", name) - } else { - bp.Printfln("name: %q,", name) - bp.Printfln("prefer: false,") - } +func (info *nativeLibInfo) generatePrebuiltLibrary(ctx android.ModuleContext, builder android.SnapshotBuilder) { // a function for emitting include dirs - printExportedDirsForNativeLibs := func(lib archSpecificNativeLibInfo, systemInclude bool) { + addExportedDirsForNativeLibs := func(lib archSpecificNativeLibInfo, properties android.BpPropertySet, systemInclude bool) { includeDirs := nativeIncludeDirPathsFor(ctx, lib, systemInclude, info.hasArchSpecificFlags) if len(includeDirs) == 0 { return } + var propertyName string if !systemInclude { - bp.Printfln("export_include_dirs: [") + propertyName = "export_include_dirs" } else { - bp.Printfln("export_system_include_dirs: [") + propertyName = "export_system_include_dirs" } - bp.Indent() - for _, dir := range includeDirs { - bp.Printfln("%q,", dir) - } - bp.Dedent() - bp.Printfln("],") + properties.AddProperty(propertyName, includeDirs) } + pbm := builder.AddPrebuiltModule(info.name, "cc_prebuilt_library_shared") + if !info.hasArchSpecificFlags { - printExportedDirsForNativeLibs(info.archVariants[0], false /*systemInclude*/) - printExportedDirsForNativeLibs(info.archVariants[0], true /*systemInclude*/) + addExportedDirsForNativeLibs(info.archVariants[0], pbm, false /*systemInclude*/) + addExportedDirsForNativeLibs(info.archVariants[0], pbm, true /*systemInclude*/) } - bp.Printfln("arch: {") - bp.Indent() + archProperties := pbm.AddPropertySet("arch") for _, av := range info.archVariants { - bp.Printfln("%s: {", av.archType) - bp.Indent() - bp.Printfln("srcs: [%q],", nativeStubFilePathFor(av)) + archTypeProperties := archProperties.AddPropertySet(av.archType) + archTypeProperties.AddProperty("srcs", []string{nativeStubFilePathFor(av)}) if info.hasArchSpecificFlags { // export_* properties are added inside the arch: {: {...}} block - printExportedDirsForNativeLibs(av, false /*systemInclude*/) - printExportedDirsForNativeLibs(av, true /*systemInclude*/) + addExportedDirsForNativeLibs(av, archTypeProperties, false /*systemInclude*/) + addExportedDirsForNativeLibs(av, archTypeProperties, true /*systemInclude*/) } - bp.Dedent() - bp.Printfln("},") // } - bp.Dedent() - bp.Printfln("},") // arch - bp.Printfln("stl: \"none\",") - bp.Printfln("system_shared_libs: [],") - bp.Dedent() - bp.Printfln("}") // cc_prebuilt_library_shared - bp.Printfln("") + pbm.AddProperty("stl", "none") + pbm.AddProperty("system_shared_libs", []string{}) } type snapshotBuilder struct { - ctx android.ModuleContext - version string - snapshotDir android.OutputPath - androidBpFile *generatedFile - filesToZip android.Paths - zipsToMerge android.Paths + ctx android.ModuleContext + version string + snapshotDir android.OutputPath + bpFile *bpFile + filesToZip android.Paths + zipsToMerge android.Paths + + prebuiltModules map[string]*bpModule + prebuiltOrder []*bpModule } func (s *snapshotBuilder) CopyToSnapshot(src android.Path, dest string) { @@ -512,10 +544,28 @@ func (s *snapshotBuilder) UnzipToSnapshot(zipPath android.Path, destDir string) s.zipsToMerge = append(s.zipsToMerge, tmpZipPath) } -func (s *snapshotBuilder) AndroidBpFile() android.GeneratedSnapshotFile { - return s.androidBpFile +func (s *snapshotBuilder) AddPrebuiltModule(name string, moduleType string) android.BpModule { + if s.prebuiltModules[name] != nil { + panic(fmt.Sprintf("Duplicate module detected, module %s has already been added", name)) + } + + m := s.bpFile.newModule(moduleType) + m.AddProperty("name", name) + + s.prebuiltModules[name] = m + s.prebuiltOrder = append(s.prebuiltOrder, m) + return m } -func (s *snapshotBuilder) VersionedSdkMemberName(unversionedName string) interface{} { +// Get a versioned name appropriate for the SDK snapshot version being taken. +func (s *snapshotBuilder) versionedSdkMemberName(unversionedName string) string { return versionedSdkMemberName(s.ctx, unversionedName, s.version) } + +func (s *snapshotBuilder) versionedSdkMemberNames(members []string) []string { + var references []string = nil + for _, m := range members { + references = append(references, s.versionedSdkMemberName(m)) + } + return references +}