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:
parent
d0fba50d71
commit
5eb7ee9fad
15 changed files with 680 additions and 3 deletions
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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())
|
||||
}
|
||||
|
||||
|
|
|
@ -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"`
|
||||
|
|
|
@ -14,6 +14,7 @@ bootstrap_go_package {
|
|||
"soong-cc",
|
||||
"soong-filesystem",
|
||||
"soong-java",
|
||||
"soong-multitree",
|
||||
"soong-provenance",
|
||||
"soong-python",
|
||||
"soong-rust",
|
||||
|
|
18
apex/apex.go
18
apex/apex.go
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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
163
cc/library_stub.go
Normal 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
108
cc/library_stub_test.go
Normal 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"])*/
|
||||
}
|
|
@ -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.
|
||||
|
|
|
@ -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
19
multitree/Android.bp
Normal 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
119
multitree/api_surface.go
Normal 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
67
multitree/export.go
Normal 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
96
multitree/import.go
Normal 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
74
multitree/metadata.go
Normal 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())
|
||||
}
|
Loading…
Reference in a new issue