Merge "Add model to represent generated snapshot .bp file"

This commit is contained in:
Paul Duffin
2019-12-03 10:07:10 +00:00
committed by Gerrit Code Review
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

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

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
}