Merge "Add model to represent generated snapshot .bp file"
diff --git a/Android.bp b/Android.bp
index 0ca11d3..6910d75 100644
--- a/Android.bp
+++ b/Android.bp
@@ -503,6 +503,7 @@
"soong-java",
],
srcs: [
+ "sdk/bp.go",
"sdk/sdk.go",
"sdk/update.go",
],
diff --git a/android/sdk.go b/android/sdk.go
index 73cb256..01e18ed 100644
--- a/android/sdk.go
+++ b/android/sdk.go
@@ -167,16 +167,39 @@
// 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 96c8416..16e6921 100644
--- a/java/droiddoc.go
+++ b/java/droiddoc.go
@@ -1979,28 +1979,6 @@
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 5cd074a..9c0fcba 100644
--- a/java/java.go
+++ b/java/java.go
@@ -1737,30 +1737,8 @@
}
}
- 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 0000000..19fb70d
--- /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 1bbd286..8def1d8 100644
--- a/sdk/sdk_test.go
+++ b/sdk/sdk_test.go
@@ -544,15 +544,9 @@
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 000d200..3bd6336 100644
--- a/sdk/update.go
+++ b/sdk/update.go
@@ -17,6 +17,7 @@
import (
"fmt"
"path/filepath"
+ "reflect"
"strings"
"github.com/google/blueprint/proptools"
@@ -28,34 +29,37 @@
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 @@
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 @@
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 @@
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 @@
}
}
- 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: {<arch>: {...}} block
- printExportedDirsForNativeLibs(av, false /*systemInclude*/)
- printExportedDirsForNativeLibs(av, true /*systemInclude*/)
+ addExportedDirsForNativeLibs(av, archTypeProperties, false /*systemInclude*/)
+ addExportedDirsForNativeLibs(av, archTypeProperties, true /*systemInclude*/)
}
- bp.Dedent()
- bp.Printfln("},") // <arch>
}
- 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 @@
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
+}