Add model to represent generated snapshot .bp file

Having each module type generate the contents of the snapshot's .bp
file results in lots of duplicated code. This adds an intermediate
model for use by the module types and then generates the .bp file
contents from that.

This not only removes the duplicated formatting code but it also
allows consistent handling of shared properties such as name further
reducing duplication. It also makes it possible to duplicate the
versioned and unversioned prebuilt modules from the same model.

Extracts generatedContents from generatedFile to allow the contents
to be populated without creating an output file, for testing.

Cleans up unused code.

Bug: 143678475
Test: m nothing
Change-Id: If21b84db0ef3fdfb5dc11ea0973ce6cb73603ea3
This commit is contained in:
Paul Duffin
2019-11-27 17:43:54 +00:00
parent 01178ed7d1
commit b645ec8e34
7 changed files with 334 additions and 169 deletions

View File

@@ -503,6 +503,7 @@ bootstrap_go_package {
"soong-java",
],
srcs: [
"sdk/bp.go",
"sdk/sdk.go",
"sdk/update.go",
],

View File

@@ -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
}

View File

@@ -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})
}

View File

@@ -1729,30 +1729,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.

141
sdk/bp.go Normal file
View File

@@ -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
}

View File

@@ -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"],
}
`)

View File

@@ -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: {<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 @@ 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
}