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
|
return c.productVariables.MissingUsesLibraries
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *config) TargetMultitreeUpdateMeta() bool {
|
||||||
|
return c.productVariables.MultitreeUpdateMeta
|
||||||
|
}
|
||||||
|
|
||||||
func (c *deviceConfig) DeviceArch() string {
|
func (c *deviceConfig) DeviceArch() string {
|
||||||
return String(c.config.productVariables.DeviceArch)
|
return String(c.config.productVariables.DeviceArch)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1057,7 +1057,8 @@ func safePathForSource(ctx PathContext, pathComponents ...string) (SourcePath, e
|
||||||
}
|
}
|
||||||
|
|
||||||
// absolute path already checked by validateSafePath
|
// 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())
|
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
|
// 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())
|
return ret, fmt.Errorf("source path %q is in output", ret.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -351,6 +351,8 @@ type productVariables struct {
|
||||||
RecoverySnapshotDirsIncluded []string `json:",omitempty"`
|
RecoverySnapshotDirsIncluded []string `json:",omitempty"`
|
||||||
HostFakeSnapshotEnabled bool `json:",omitempty"`
|
HostFakeSnapshotEnabled bool `json:",omitempty"`
|
||||||
|
|
||||||
|
MultitreeUpdateMeta bool `json:",omitempty"`
|
||||||
|
|
||||||
BoardVendorSepolicyDirs []string `json:",omitempty"`
|
BoardVendorSepolicyDirs []string `json:",omitempty"`
|
||||||
BoardOdmSepolicyDirs []string `json:",omitempty"`
|
BoardOdmSepolicyDirs []string `json:",omitempty"`
|
||||||
BoardReqdMaskPolicy []string `json:",omitempty"`
|
BoardReqdMaskPolicy []string `json:",omitempty"`
|
||||||
|
|
|
@ -14,6 +14,7 @@ bootstrap_go_package {
|
||||||
"soong-cc",
|
"soong-cc",
|
||||||
"soong-filesystem",
|
"soong-filesystem",
|
||||||
"soong-java",
|
"soong-java",
|
||||||
|
"soong-multitree",
|
||||||
"soong-provenance",
|
"soong-provenance",
|
||||||
"soong-python",
|
"soong-python",
|
||||||
"soong-rust",
|
"soong-rust",
|
||||||
|
|
18
apex/apex.go
18
apex/apex.go
|
@ -33,6 +33,7 @@ import (
|
||||||
prebuilt_etc "android/soong/etc"
|
prebuilt_etc "android/soong/etc"
|
||||||
"android/soong/filesystem"
|
"android/soong/filesystem"
|
||||||
"android/soong/java"
|
"android/soong/java"
|
||||||
|
"android/soong/multitree"
|
||||||
"android/soong/python"
|
"android/soong/python"
|
||||||
"android/soong/rust"
|
"android/soong/rust"
|
||||||
"android/soong/sh"
|
"android/soong/sh"
|
||||||
|
@ -358,6 +359,7 @@ type apexBundle struct {
|
||||||
android.OverridableModuleBase
|
android.OverridableModuleBase
|
||||||
android.SdkBase
|
android.SdkBase
|
||||||
android.BazelModuleBase
|
android.BazelModuleBase
|
||||||
|
multitree.ExportableModuleBase
|
||||||
|
|
||||||
// Properties
|
// Properties
|
||||||
properties apexBundleProperties
|
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)
|
var _ cc.Coverage = (*apexBundle)(nil)
|
||||||
|
|
||||||
// Implements cc.Coverage
|
// Implements cc.Coverage
|
||||||
|
@ -2372,6 +2389,7 @@ func newApexBundle() *apexBundle {
|
||||||
android.InitSdkAwareModule(module)
|
android.InitSdkAwareModule(module)
|
||||||
android.InitOverridableModule(module, &module.overridableProperties.Overrides)
|
android.InitOverridableModule(module, &module.overridableProperties.Overrides)
|
||||||
android.InitBazelModule(module)
|
android.InitBazelModule(module)
|
||||||
|
multitree.InitExportableModule(module)
|
||||||
return module
|
return module
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,6 +15,7 @@ bootstrap_go_package {
|
||||||
"soong-etc",
|
"soong-etc",
|
||||||
"soong-fuzz",
|
"soong-fuzz",
|
||||||
"soong-genrule",
|
"soong-genrule",
|
||||||
|
"soong-multitree",
|
||||||
"soong-snapshot",
|
"soong-snapshot",
|
||||||
"soong-tradefed",
|
"soong-tradefed",
|
||||||
],
|
],
|
||||||
|
@ -65,6 +66,7 @@ bootstrap_go_package {
|
||||||
"library.go",
|
"library.go",
|
||||||
"library_headers.go",
|
"library_headers.go",
|
||||||
"library_sdk_member.go",
|
"library_sdk_member.go",
|
||||||
|
"library_stub.go",
|
||||||
"native_bridge_sdk_trait.go",
|
"native_bridge_sdk_trait.go",
|
||||||
"object.go",
|
"object.go",
|
||||||
"test.go",
|
"test.go",
|
||||||
|
@ -94,6 +96,7 @@ bootstrap_go_package {
|
||||||
"gen_test.go",
|
"gen_test.go",
|
||||||
"genrule_test.go",
|
"genrule_test.go",
|
||||||
"library_headers_test.go",
|
"library_headers_test.go",
|
||||||
|
"library_stub_test.go",
|
||||||
"library_test.go",
|
"library_test.go",
|
||||||
"object_test.go",
|
"object_test.go",
|
||||||
"prebuilt_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 {
|
type libraryProperties struct {
|
||||||
// Relative path to the symbol map.
|
// Relative path to the symbol map.
|
||||||
// An example file can be seen here: TODO(danalbert): Make an example.
|
// 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
|
// The first API level a library was available. A library will be generated
|
||||||
// for every API level beginning with this one.
|
// for every API level beginning with this one.
|
||||||
|
|
|
@ -29,6 +29,7 @@ func RegisterRequiredBuildComponentsForTest(ctx android.RegistrationContext) {
|
||||||
RegisterBinaryBuildComponents(ctx)
|
RegisterBinaryBuildComponents(ctx)
|
||||||
RegisterLibraryBuildComponents(ctx)
|
RegisterLibraryBuildComponents(ctx)
|
||||||
RegisterLibraryHeadersBuildComponents(ctx)
|
RegisterLibraryHeadersBuildComponents(ctx)
|
||||||
|
RegisterLibraryStubBuildComponents(ctx)
|
||||||
|
|
||||||
ctx.RegisterModuleType("cc_benchmark", BenchmarkFactory)
|
ctx.RegisterModuleType("cc_benchmark", BenchmarkFactory)
|
||||||
ctx.RegisterModuleType("cc_object", ObjectFactory)
|
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