Prototype changes for multitree

This change contains a prototype implementation for multitree. Several
interfaces and modules are added.

1. Imported/Exported
Modules implementing Exportable interface can export artifacts to other
components. "imported_filegroup" modules can import generated artifacts from other exported modules.

2. Multitree metadata
It contains information about imported/exported modules in each
component, and can be generated via "m update-meta".

3. cc library stub
It's based on prototype stub libraries. It uses imported/exported
mechanism to expose a C API, with a map.txt file and header files.

Bug: 230448564
Test: m
Change-Id: Id7ff7618e2c630c5617a564d8b23b60a1cc9c8e8
This commit is contained in:
Inseob Kim 2022-04-27 10:30:34 +09:00
parent d0fba50d71
commit 5eb7ee9fad
15 changed files with 680 additions and 3 deletions

View file

@ -1482,6 +1482,10 @@ func (c *config) MissingUsesLibraries() []string {
return c.productVariables.MissingUsesLibraries
}
func (c *config) TargetMultitreeUpdateMeta() bool {
return c.productVariables.MultitreeUpdateMeta
}
func (c *deviceConfig) DeviceArch() string {
return String(c.config.productVariables.DeviceArch)
}

View file

@ -1057,7 +1057,8 @@ func safePathForSource(ctx PathContext, pathComponents ...string) (SourcePath, e
}
// absolute path already checked by validateSafePath
if strings.HasPrefix(ret.String(), ctx.Config().soongOutDir) {
// special-case api surface gen files for now
if strings.HasPrefix(ret.String(), ctx.Config().soongOutDir) && !strings.Contains(ret.String(), ctx.Config().soongOutDir+"/.export") {
return ret, fmt.Errorf("source path %q is in output", ret.String())
}
@ -1073,7 +1074,8 @@ func pathForSource(ctx PathContext, pathComponents ...string) (SourcePath, error
}
// absolute path already checked by validatePath
if strings.HasPrefix(ret.String(), ctx.Config().soongOutDir) {
// special-case for now
if strings.HasPrefix(ret.String(), ctx.Config().soongOutDir) && !strings.Contains(ret.String(), ctx.Config().soongOutDir+"/.export") {
return ret, fmt.Errorf("source path %q is in output", ret.String())
}

View file

@ -351,6 +351,8 @@ type productVariables struct {
RecoverySnapshotDirsIncluded []string `json:",omitempty"`
HostFakeSnapshotEnabled bool `json:",omitempty"`
MultitreeUpdateMeta bool `json:",omitempty"`
BoardVendorSepolicyDirs []string `json:",omitempty"`
BoardOdmSepolicyDirs []string `json:",omitempty"`
BoardReqdMaskPolicy []string `json:",omitempty"`

View file

@ -14,6 +14,7 @@ bootstrap_go_package {
"soong-cc",
"soong-filesystem",
"soong-java",
"soong-multitree",
"soong-provenance",
"soong-python",
"soong-rust",

View file

@ -33,6 +33,7 @@ import (
prebuilt_etc "android/soong/etc"
"android/soong/filesystem"
"android/soong/java"
"android/soong/multitree"
"android/soong/python"
"android/soong/rust"
"android/soong/sh"
@ -358,6 +359,7 @@ type apexBundle struct {
android.OverridableModuleBase
android.SdkBase
android.BazelModuleBase
multitree.ExportableModuleBase
// Properties
properties apexBundleProperties
@ -1359,6 +1361,21 @@ func (a *apexBundle) OutputFiles(tag string) (android.Paths, error) {
}
}
var _ multitree.Exportable = (*apexBundle)(nil)
func (a *apexBundle) Exportable() bool {
if a.properties.ApexType == flattenedApex {
return false
}
return true
}
func (a *apexBundle) TaggedOutputs() map[string]android.Paths {
ret := make(map[string]android.Paths)
ret["apex"] = android.Paths{a.outputFile}
return ret
}
var _ cc.Coverage = (*apexBundle)(nil)
// Implements cc.Coverage
@ -2372,6 +2389,7 @@ func newApexBundle() *apexBundle {
android.InitSdkAwareModule(module)
android.InitOverridableModule(module, &module.overridableProperties.Overrides)
android.InitBazelModule(module)
multitree.InitExportableModule(module)
return module
}

View file

@ -15,6 +15,7 @@ bootstrap_go_package {
"soong-etc",
"soong-fuzz",
"soong-genrule",
"soong-multitree",
"soong-snapshot",
"soong-tradefed",
],
@ -65,6 +66,7 @@ bootstrap_go_package {
"library.go",
"library_headers.go",
"library_sdk_member.go",
"library_stub.go",
"native_bridge_sdk_trait.go",
"object.go",
"test.go",
@ -94,6 +96,7 @@ bootstrap_go_package {
"gen_test.go",
"genrule_test.go",
"library_headers_test.go",
"library_stub_test.go",
"library_test.go",
"object_test.go",
"prebuilt_test.go",

163
cc/library_stub.go Normal file
View file

@ -0,0 +1,163 @@
// 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 (
"android/soong/android"
"android/soong/multitree"
)
func init() {
RegisterLibraryStubBuildComponents(android.InitRegistrationContext)
}
func RegisterLibraryStubBuildComponents(ctx android.RegistrationContext) {
// cc_api_stub_library shares a lot of ndk_library, and this will be refactored later
ctx.RegisterModuleType("cc_api_stub_library", CcApiStubLibraryFactory)
ctx.RegisterModuleType("cc_api_contribution", CcApiContributionFactory)
}
func CcApiStubLibraryFactory() android.Module {
module, decorator := NewLibrary(android.DeviceSupported)
apiStubDecorator := &apiStubDecorator{
libraryDecorator: decorator,
}
apiStubDecorator.BuildOnlyShared()
module.compiler = apiStubDecorator
module.linker = apiStubDecorator
module.installer = nil
module.library = apiStubDecorator
module.Properties.HideFromMake = true // TODO: remove
android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibBoth)
module.AddProperties(&module.Properties,
&apiStubDecorator.properties,
&apiStubDecorator.MutatedProperties,
&apiStubDecorator.apiStubLibraryProperties)
return module
}
type apiStubLiraryProperties struct {
Imported_includes []string `android:"path"`
}
type apiStubDecorator struct {
*libraryDecorator
properties libraryProperties
apiStubLibraryProperties apiStubLiraryProperties
}
func (compiler *apiStubDecorator) stubsVersions(ctx android.BaseMutatorContext) []string {
firstVersion := String(compiler.properties.First_version)
return ndkLibraryVersions(ctx, android.ApiLevelOrPanic(ctx, firstVersion))
}
func (decorator *apiStubDecorator) compile(ctx ModuleContext, flags Flags, deps PathDeps) Objects {
if decorator.stubsVersion() == "" {
decorator.setStubsVersion("current")
} // TODO: fix
symbolFile := String(decorator.properties.Symbol_file)
nativeAbiResult := parseNativeAbiDefinition(ctx, symbolFile,
android.ApiLevelOrPanic(ctx, decorator.stubsVersion()),
"")
return compileStubLibrary(ctx, flags, nativeAbiResult.stubSrc)
}
func (decorator *apiStubDecorator) link(ctx ModuleContext, flags Flags, deps PathDeps, objects Objects) android.Path {
decorator.reexportDirs(android.PathsForModuleSrc(ctx, decorator.apiStubLibraryProperties.Imported_includes)...)
return decorator.libraryDecorator.link(ctx, flags, deps, objects)
}
func init() {
pctx.HostBinToolVariable("gen_api_surface_build_files", "gen_api_surface_build_files")
}
type CcApiContribution struct {
android.ModuleBase
properties ccApiContributionProperties
}
type ccApiContributionProperties struct {
Symbol_file *string `android:"path"`
First_version *string
Export_include_dir *string
}
func CcApiContributionFactory() android.Module {
module := &CcApiContribution{}
module.AddProperties(&module.properties)
android.InitAndroidModule(module)
return module
}
// Do some simple validations
// Majority of the build rules will be created in the ctx of the api surface this module contributes to
func (contrib *CcApiContribution) GenerateAndroidBuildActions(ctx android.ModuleContext) {
if contrib.properties.Symbol_file == nil {
ctx.PropertyErrorf("symbol_file", "%v does not have symbol file", ctx.ModuleName())
}
if contrib.properties.First_version == nil {
ctx.PropertyErrorf("first_version", "%v does not have first_version for stub variants", ctx.ModuleName())
}
}
// Path is out/soong/.export/ but will be different in final multi-tree layout
func outPathApiSurface(ctx android.ModuleContext, myModuleName string, pathComponent string) android.OutputPath {
return android.PathForOutput(ctx, ".export", ctx.ModuleName(), myModuleName, pathComponent)
}
func (contrib *CcApiContribution) CopyFilesWithTag(apiSurfaceContext android.ModuleContext) map[string]android.Paths {
// copy map.txt for now
// hardlinks cannot be created since nsjail creates a different mountpoint for out/
myDir := apiSurfaceContext.OtherModuleDir(contrib)
genMapTxt := outPathApiSurface(apiSurfaceContext, contrib.Name(), String(contrib.properties.Symbol_file))
apiSurfaceContext.Build(pctx, android.BuildParams{
Rule: android.Cp,
Description: "import map.txt file",
Input: android.PathForSource(apiSurfaceContext, myDir, String(contrib.properties.Symbol_file)),
Output: genMapTxt,
})
outputs := make(map[string]android.Paths)
outputs["map"] = []android.Path{genMapTxt}
if contrib.properties.Export_include_dir != nil {
includeDir := android.PathForSource(apiSurfaceContext, myDir, String(contrib.properties.Export_include_dir))
outputs["export_include_dir"] = []android.Path{includeDir}
}
return outputs
}
var _ multitree.ApiContribution = (*CcApiContribution)(nil)
/*
func (contrib *CcApiContribution) GenerateBuildFiles(apiSurfaceContext android.ModuleContext) android.Paths {
genAndroidBp := outPathApiSurface(apiSurfaceContext, contrib.Name(), "Android.bp")
// generate Android.bp
apiSurfaceContext.Build(pctx, android.BuildParams{
Rule: genApiSurfaceBuildFiles,
Description: "generate API surface build files",
Outputs: []android.WritablePath{genAndroidBp},
Args: map[string]string{
"name": contrib.Name() + "." + apiSurfaceContext.ModuleName(), //e.g. liblog.ndk
"symbol_file": String(contrib.properties.Symbol_file),
"first_version": String(contrib.properties.First_version),
},
})
return []android.Path{genAndroidBp}
}
*/

108
cc/library_stub_test.go Normal file
View file

@ -0,0 +1,108 @@
// 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 (
_ "fmt"
_ "sort"
"testing"
"android/soong/android"
"android/soong/multitree"
)
func TestCcApiStubLibraryOutputFiles(t *testing.T) {
bp := `
cc_api_stub_library {
name: "foo",
symbol_file: "foo.map.txt",
first_version: "29",
}
`
result := prepareForCcTest.RunTestWithBp(t, bp)
outputs := result.ModuleForTests("foo", "android_arm64_armv8-a_shared").AllOutputs()
expected_file_suffixes := []string{".c", "stub.map", ".o", ".so"}
for _, expected_file_suffix := range expected_file_suffixes {
android.AssertBoolEquals(t, expected_file_suffix+" file not found in output", true, android.SuffixInList(outputs, expected_file_suffix))
}
}
func TestCcApiStubLibraryVariants(t *testing.T) {
bp := `
cc_api_stub_library {
name: "foo",
symbol_file: "foo.map.txt",
first_version: "29",
}
`
result := prepareForCcTest.RunTestWithBp(t, bp)
variants := result.ModuleVariantsForTests("foo")
expected_variants := []string{"29", "30", "S", "Tiramisu"} //TODO: make this test deterministic by using fixtures
for _, expected_variant := range expected_variants {
android.AssertBoolEquals(t, expected_variant+" variant not found in foo", true, android.SubstringInList(variants, expected_variant))
}
}
func TestCcLibraryUsesCcApiStubLibrary(t *testing.T) {
bp := `
cc_api_stub_library {
name: "foo",
symbol_file: "foo.map.txt",
first_version: "29",
}
cc_library {
name: "foo_user",
shared_libs: [
"foo#29",
],
}
`
prepareForCcTest.RunTestWithBp(t, bp)
}
func TestApiSurfaceOutputs(t *testing.T) {
bp := `
api_surface {
name: "mysdk",
contributions: [
"foo",
],
}
cc_api_contribution {
name: "foo",
symbol_file: "foo.map.txt",
first_version: "29",
}
`
result := android.GroupFixturePreparers(
prepareForCcTest,
multitree.PrepareForTestWithApiSurface,
).RunTestWithBp(t, bp)
mysdk := result.ModuleForTests("mysdk", "")
actual_surface_inputs := mysdk.Rule("phony").BuildParams.Inputs.Strings()
expected_file_suffixes := []string{"mysdk/foo/foo.map.txt"}
for _, expected_file_suffix := range expected_file_suffixes {
android.AssertBoolEquals(t, expected_file_suffix+" file not found in input", true, android.SuffixInList(actual_surface_inputs, expected_file_suffix))
}
// check args/inputs to rule
/*api_surface_gen_rule_args := result.ModuleForTests("mysdk", "").Rule("genApiSurfaceBuildFiles").Args
android.AssertStringEquals(t, "name", "foo.mysdk", api_surface_gen_rule_args["name"])
android.AssertStringEquals(t, "symbol_file", "foo.map.txt", api_surface_gen_rule_args["symbol_file"])*/
}

View file

@ -93,7 +93,7 @@ var (
type libraryProperties struct {
// Relative path to the symbol map.
// An example file can be seen here: TODO(danalbert): Make an example.
Symbol_file *string
Symbol_file *string `android:"path"`
// The first API level a library was available. A library will be generated
// for every API level beginning with this one.

View file

@ -29,6 +29,7 @@ func RegisterRequiredBuildComponentsForTest(ctx android.RegistrationContext) {
RegisterBinaryBuildComponents(ctx)
RegisterLibraryBuildComponents(ctx)
RegisterLibraryHeadersBuildComponents(ctx)
RegisterLibraryStubBuildComponents(ctx)
ctx.RegisterModuleType("cc_benchmark", BenchmarkFactory)
ctx.RegisterModuleType("cc_object", ObjectFactory)

19
multitree/Android.bp Normal file
View file

@ -0,0 +1,19 @@
package {
default_applicable_licenses: ["Android-Apache-2.0"],
}
bootstrap_go_package {
name: "soong-multitree",
pkgPath: "android/soong/multitree",
deps: [
"blueprint",
"soong-android",
],
srcs: [
"api_surface.go",
"export.go",
"metadata.go",
"import.go",
],
pluginFor: ["soong_build"],
}

119
multitree/api_surface.go Normal file
View file

@ -0,0 +1,119 @@
// 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 multitree
import (
"android/soong/android"
"fmt"
"github.com/google/blueprint"
)
var (
pctx = android.NewPackageContext("android/soong/multitree")
)
func init() {
RegisterApiSurfaceBuildComponents(android.InitRegistrationContext)
}
var PrepareForTestWithApiSurface = android.FixtureRegisterWithContext(RegisterApiSurfaceBuildComponents)
func RegisterApiSurfaceBuildComponents(ctx android.RegistrationContext) {
ctx.RegisterModuleType("api_surface", ApiSurfaceFactory)
}
type ApiSurface struct {
android.ModuleBase
ExportableModuleBase
properties apiSurfaceProperties
allOutputs android.Paths
taggedOutputs map[string]android.Paths
}
type apiSurfaceProperties struct {
Contributions []string
}
func ApiSurfaceFactory() android.Module {
module := &ApiSurface{}
module.AddProperties(&module.properties)
android.InitAndroidModule(module)
InitExportableModule(module)
return module
}
func (surface *ApiSurface) DepsMutator(ctx android.BottomUpMutatorContext) {
if surface.properties.Contributions != nil {
ctx.AddVariationDependencies(nil, nil, surface.properties.Contributions...)
}
}
func (surface *ApiSurface) GenerateAndroidBuildActions(ctx android.ModuleContext) {
contributionFiles := make(map[string]android.Paths)
var allOutputs android.Paths
ctx.WalkDeps(func(child, parent android.Module) bool {
if contribution, ok := child.(ApiContribution); ok {
copied := contribution.CopyFilesWithTag(ctx)
for tag, files := range copied {
contributionFiles[child.Name()+"#"+tag] = files
}
for _, paths := range copied {
allOutputs = append(allOutputs, paths...)
}
return false // no transitive dependencies
}
return false
})
// phony target
ctx.Build(pctx, android.BuildParams{
Rule: blueprint.Phony,
Output: android.PathForPhony(ctx, ctx.ModuleName()),
Inputs: allOutputs,
})
surface.allOutputs = allOutputs
surface.taggedOutputs = contributionFiles
}
func (surface *ApiSurface) OutputFiles(tag string) (android.Paths, error) {
if tag != "" {
return nil, fmt.Errorf("unknown tag: %q", tag)
}
return surface.allOutputs, nil
}
func (surface *ApiSurface) TaggedOutputs() map[string]android.Paths {
return surface.taggedOutputs
}
func (surface *ApiSurface) Exportable() bool {
return true
}
var _ android.OutputFileProducer = (*ApiSurface)(nil)
var _ Exportable = (*ApiSurface)(nil)
type ApiContribution interface {
// copy files necessaryt to construct an API surface
// For C, it will be map.txt and .h files
// For Java, it will be api.txt
CopyFilesWithTag(ctx android.ModuleContext) map[string]android.Paths // output paths
// Generate Android.bp in out/ to use the exported .txt files
// GenerateBuildFiles(ctx ModuleContext) Paths //output paths
}

67
multitree/export.go Normal file
View file

@ -0,0 +1,67 @@
// Copyright 2022 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 multitree
import (
"android/soong/android"
"github.com/google/blueprint/proptools"
)
type moduleExportProperty struct {
// True if the module is exported to the other components in a multi-tree.
// Any components in the multi-tree can import this module to use.
Export *bool
}
type ExportableModuleBase struct {
properties moduleExportProperty
}
type Exportable interface {
// Properties for the exporable module.
exportableModuleProps() *moduleExportProperty
// Check if this module can be exported.
// If this returns false, the module will not be exported regardless of the 'export' value.
Exportable() bool
// Returns 'true' if this module has 'export: true'
// This module will not be exported if it returns 'false' to 'Exportable()' interface even if
// it has 'export: true'.
IsExported() bool
// Map from tags to outputs.
// Each module can tag their outputs for convenience.
TaggedOutputs() map[string]android.Paths
}
type ExportableModule interface {
android.Module
android.OutputFileProducer
Exportable
}
func InitExportableModule(module ExportableModule) {
module.AddProperties(module.exportableModuleProps())
}
func (m *ExportableModuleBase) exportableModuleProps() *moduleExportProperty {
return &m.properties
}
func (m *ExportableModuleBase) IsExported() bool {
return proptools.Bool(m.properties.Export)
}

96
multitree/import.go Normal file
View file

@ -0,0 +1,96 @@
// Copyright 2022 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 multitree
import (
"android/soong/android"
)
var (
nameSuffix = ".imported"
)
type MultitreeImportedModuleInterface interface {
GetMultitreeImportedModuleName() string
}
func init() {
android.RegisterModuleType("imported_filegroup", importedFileGroupFactory)
android.PreArchMutators(RegisterMultitreePreArchMutators)
}
type importedFileGroupProperties struct {
// Imported modules from the other components in a multi-tree
Imported []string
}
type importedFileGroup struct {
android.ModuleBase
properties importedFileGroupProperties
srcs android.Paths
}
func (ifg *importedFileGroup) Name() string {
return ifg.BaseModuleName() + nameSuffix
}
func importedFileGroupFactory() android.Module {
module := &importedFileGroup{}
module.AddProperties(&module.properties)
android.InitAndroidModule(module)
return module
}
var _ MultitreeImportedModuleInterface = (*importedFileGroup)(nil)
func (ifg *importedFileGroup) GetMultitreeImportedModuleName() string {
// The base module name of the imported filegroup is used as the imported module name
return ifg.BaseModuleName()
}
var _ android.SourceFileProducer = (*importedFileGroup)(nil)
func (ifg *importedFileGroup) Srcs() android.Paths {
return ifg.srcs
}
func (ifg *importedFileGroup) GenerateAndroidBuildActions(ctx android.ModuleContext) {
// srcs from this module must not be used. Adding a dot path to avoid the empty
// source failure. Still soong returns error when a module wants to build against
// this source, which is intended.
ifg.srcs = android.PathsForModuleSrc(ctx, []string{"."})
}
func RegisterMultitreePreArchMutators(ctx android.RegisterMutatorsContext) {
ctx.BottomUp("multitree_imported_rename", MultitreeImportedRenameMutator).Parallel()
}
func MultitreeImportedRenameMutator(ctx android.BottomUpMutatorContext) {
if m, ok := ctx.Module().(MultitreeImportedModuleInterface); ok {
name := m.GetMultitreeImportedModuleName()
if !ctx.OtherModuleExists(name) {
// Provide an empty filegroup not to break the build while updating the metadata.
// In other cases, soong will report an error to guide users to run 'm update-meta'
// first.
if !ctx.Config().TargetMultitreeUpdateMeta() {
ctx.ModuleErrorf("\"%s\" filegroup must be imported.\nRun 'm update-meta' first to import the filegroup.", name)
}
ctx.Rename(name)
}
}
}

74
multitree/metadata.go Normal file
View file

@ -0,0 +1,74 @@
// Copyright 2022 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 multitree
import (
"android/soong/android"
"encoding/json"
)
func init() {
android.RegisterSingletonType("update-meta", UpdateMetaSingleton)
}
func UpdateMetaSingleton() android.Singleton {
return &updateMetaSingleton{}
}
type jsonImported struct {
FileGroups map[string][]string `json:",omitempty"`
}
type metadataJsonFlags struct {
Imported jsonImported `json:",omitempty"`
Exported map[string][]string `json:",omitempty"`
}
type updateMetaSingleton struct {
importedModules []string
generatedMetadataFile android.OutputPath
}
func (s *updateMetaSingleton) GenerateBuildActions(ctx android.SingletonContext) {
metadata := metadataJsonFlags{
Imported: jsonImported{
FileGroups: make(map[string][]string),
},
Exported: make(map[string][]string),
}
ctx.VisitAllModules(func(module android.Module) {
if ifg, ok := module.(*importedFileGroup); ok {
metadata.Imported.FileGroups[ifg.BaseModuleName()] = ifg.properties.Imported
}
if e, ok := module.(ExportableModule); ok {
if e.IsExported() && e.Exportable() {
for tag, files := range e.TaggedOutputs() {
// TODO(b/219846705): refactor this to a dictionary
metadata.Exported[e.Name()+":"+tag] = append(metadata.Exported[e.Name()+":"+tag], files.Strings()...)
}
}
}
})
jsonStr, err := json.Marshal(metadata)
if err != nil {
ctx.Errorf(err.Error())
}
s.generatedMetadataFile = android.PathForOutput(ctx, "multitree", "metadata.json")
android.WriteFileRule(ctx, s.generatedMetadataFile, string(jsonStr))
}
func (s *updateMetaSingleton) MakeVars(ctx android.MakeVarsContext) {
ctx.Strict("MULTITREE_METADATA", s.generatedMetadataFile.String())
}