Merge changes I8e4c3f37,I89a32bde
* changes:
add plugins to javaLibraryAttributes
convert java_import_host with bp2build; enable jetifier
diff --git a/android/Android.bp b/android/Android.bp
index d3540b2..c072ac2 100644
--- a/android/Android.bp
+++ b/android/Android.bp
@@ -8,6 +8,7 @@
deps: [
"blueprint",
"blueprint-bootstrap",
+ "blueprint-metrics",
"sbox_proto",
"soong",
"soong-android-soongconfig",
diff --git a/android/androidmk.go b/android/androidmk.go
index 72b6584..e9c63fb 100644
--- a/android/androidmk.go
+++ b/android/androidmk.go
@@ -148,6 +148,14 @@
// without worrying about the variables being mixed up in the actual mk file.
// 3. Makes troubleshooting and spotting errors easier.
entryOrder []string
+
+ // Provides data typically stored by Context objects that are commonly needed by
+ //AndroidMkEntries objects.
+ entryContext AndroidMkEntriesContext
+}
+
+type AndroidMkEntriesContext interface {
+ Config() Config
}
type AndroidMkExtraEntriesContext interface {
@@ -408,10 +416,19 @@
}
}
+ ext := filepath.Ext(dest)
+ suffix := ""
if dist.Suffix != nil {
- ext := filepath.Ext(dest)
- suffix := *dist.Suffix
- dest = strings.TrimSuffix(dest, ext) + suffix + ext
+ suffix = *dist.Suffix
+ }
+
+ productString := ""
+ if dist.Append_artifact_with_product != nil && *dist.Append_artifact_with_product {
+ productString = fmt.Sprintf("_%s", a.entryContext.Config().DeviceProduct())
+ }
+
+ if suffix != "" || productString != "" {
+ dest = strings.TrimSuffix(dest, ext) + suffix + productString + ext
}
if dist.Dir != nil {
@@ -478,6 +495,7 @@
}
func (a *AndroidMkEntries) fillInEntries(ctx fillInEntriesContext, mod blueprint.Module) {
+ a.entryContext = ctx
a.EntryMap = make(map[string][]string)
amod := mod.(Module)
base := amod.base()
diff --git a/android/androidmk_test.go b/android/androidmk_test.go
index ecfb008..caf11f1 100644
--- a/android/androidmk_test.go
+++ b/android/androidmk_test.go
@@ -148,6 +148,9 @@
FixtureRegisterWithContext(func(ctx RegistrationContext) {
ctx.RegisterModuleType("custom", customModuleFactory)
}),
+ FixtureModifyProductVariables(func(variables FixtureProductVariables) {
+ variables.DeviceProduct = proptools.StringPtr("bar")
+ }),
FixtureWithRootAndroidBp(bp),
).RunTest(t)
@@ -400,6 +403,25 @@
},
})
+ testHelper(t, "append-artifact-with-product", `
+ custom {
+ name: "foo",
+ dist: {
+ targets: ["my_goal"],
+ append_artifact_with_product: true,
+ }
+ }
+`, &distContributions{
+ copiesForGoals: []*copiesForGoals{
+ {
+ goals: "my_goal",
+ copies: []distCopy{
+ distCopyForTest("one.out", "one_bar.out"),
+ },
+ },
+ },
+ })
+
testHelper(t, "dists-with-tag", `
custom {
name: "foo",
diff --git a/android/bazel.go b/android/bazel.go
index 372fe8c..fafb68b 100644
--- a/android/bazel.go
+++ b/android/bazel.go
@@ -513,6 +513,9 @@
"brotli-fuzzer-corpus", // b/202015218: outputs are in location incompatible with bazel genrule handling.
+ // python modules
+ "analyze_bcpf", // depends on bpmodify a blueprint_go_binary.
+
// b/203369847: multiple genrules in the same package creating the same file
// //development/sdk/...
"platform_tools_properties",
diff --git a/android/config.go b/android/config.go
index e8ca84c..5c41ee8 100644
--- a/android/config.go
+++ b/android/config.go
@@ -351,6 +351,7 @@
config := &config{
productVariables: productVariables{
DeviceName: stringPtr("test_device"),
+ DeviceProduct: stringPtr("test_product"),
Platform_sdk_version: intPtr(30),
Platform_sdk_codename: stringPtr("S"),
Platform_base_sdk_extension_version: intPtr(1),
@@ -723,6 +724,15 @@
return *c.productVariables.DeviceName
}
+// DeviceProduct returns the current product target. There could be multiple of
+// these per device type.
+//
+// NOTE: Do not base conditional logic on this value. It may break product
+// inheritance.
+func (c *config) DeviceProduct() string {
+ return *c.productVariables.DeviceProduct
+}
+
func (c *config) DeviceResourceOverlays() []string {
return c.productVariables.DeviceResourceOverlays
}
diff --git a/android/metrics.go b/android/metrics.go
index 2cd5efa..9038bde 100644
--- a/android/metrics.go
+++ b/android/metrics.go
@@ -18,6 +18,7 @@
"io/ioutil"
"runtime"
+ "github.com/google/blueprint/metrics"
"google.golang.org/protobuf/proto"
soong_metrics_proto "android/soong/ui/metrics/metrics_proto"
@@ -55,7 +56,7 @@
})
}
-func collectMetrics(config Config) *soong_metrics_proto.SoongBuildMetrics {
+func collectMetrics(config Config, eventHandler metrics.EventHandler) *soong_metrics_proto.SoongBuildMetrics {
metrics := &soong_metrics_proto.SoongBuildMetrics{}
soongMetrics := ReadSoongMetrics(config)
@@ -68,11 +69,21 @@
metrics.TotalAllocCount = proto.Uint64(memStats.Mallocs)
metrics.TotalAllocSize = proto.Uint64(memStats.TotalAlloc)
+ for _, event := range eventHandler.CompletedEvents() {
+ perfInfo := soong_metrics_proto.PerfInfo{
+ Description: proto.String(event.Id),
+ Name: proto.String("soong_build"),
+ StartTime: proto.Uint64(uint64(event.Start.UnixNano())),
+ RealTime: proto.Uint64(event.RuntimeNanoseconds()),
+ }
+ metrics.Events = append(metrics.Events, &perfInfo)
+ }
+
return metrics
}
-func WriteMetrics(config Config, metricsFile string) error {
- metrics := collectMetrics(config)
+func WriteMetrics(config Config, eventHandler metrics.EventHandler, metricsFile string) error {
+ metrics := collectMetrics(config, eventHandler)
buf, err := proto.Marshal(metrics)
if err != nil {
diff --git a/android/module.go b/android/module.go
index 43509c0..66a5f60 100644
--- a/android/module.go
+++ b/android/module.go
@@ -613,6 +613,12 @@
// A suffix to add to the artifact file name (before any extension).
Suffix *string `android:"arch_variant"`
+ // If true, then the artifact file will be appended with _<product name>. For
+ // example, if the product is coral and the module is an android_app module
+ // of name foo, then the artifact would be foo_coral.apk. If false, there is
+ // no change to the artifact file name.
+ Append_artifact_with_product *bool `android:"arch_variant"`
+
// A string tag to select the OutputFiles associated with the tag.
//
// If no tag is specified then it will select the default dist paths provided
@@ -1464,8 +1470,10 @@
}
type propInfo struct {
- Name string
- Type string
+ Name string
+ Type string
+ Value string
+ Values []string
}
func (m *ModuleBase) propertiesWithValues() []propInfo {
@@ -1505,18 +1513,60 @@
return
}
elKind := v.Type().Elem().Kind()
- info = append(info, propInfo{name, elKind.String() + " " + kind.String()})
+ info = append(info, propInfo{Name: name, Type: elKind.String() + " " + kind.String(), Values: sliceReflectionValue(v)})
default:
- info = append(info, propInfo{name, kind.String()})
+ info = append(info, propInfo{Name: name, Type: kind.String(), Value: reflectionValue(v)})
}
}
for _, p := range props {
propsWithValues("", reflect.ValueOf(p).Elem())
}
+ sort.Slice(info, func(i, j int) bool {
+ return info[i].Name < info[j].Name
+ })
return info
}
+func reflectionValue(value reflect.Value) string {
+ switch value.Kind() {
+ case reflect.Bool:
+ return fmt.Sprintf("%t", value.Bool())
+ case reflect.Int64:
+ return fmt.Sprintf("%d", value.Int())
+ case reflect.String:
+ return fmt.Sprintf("%s", value.String())
+ case reflect.Struct:
+ if value.IsZero() {
+ return "{}"
+ }
+ length := value.NumField()
+ vals := make([]string, length, length)
+ for i := 0; i < length; i++ {
+ sTyp := value.Type().Field(i)
+ if proptools.ShouldSkipProperty(sTyp) {
+ continue
+ }
+ name := sTyp.Name
+ vals[i] = fmt.Sprintf("%s: %s", name, reflectionValue(value.Field(i)))
+ }
+ return fmt.Sprintf("%s{%s}", value.Type(), strings.Join(vals, ", "))
+ case reflect.Array, reflect.Slice:
+ vals := sliceReflectionValue(value)
+ return fmt.Sprintf("[%s]", strings.Join(vals, ", "))
+ }
+ return ""
+}
+
+func sliceReflectionValue(value reflect.Value) []string {
+ length := value.Len()
+ vals := make([]string, length, length)
+ for i := 0; i < length; i++ {
+ vals[i] = reflectionValue(value.Index(i))
+ }
+ return vals
+}
+
func (m *ModuleBase) ComponentDepsMutator(BottomUpMutatorContext) {}
func (m *ModuleBase) DepsMutator(BottomUpMutatorContext) {}
@@ -3113,6 +3163,7 @@
symlinkTarget: "",
executable: executable,
effectiveLicenseFiles: &licenseFiles,
+ partition: fullInstallPath.partition,
}
m.packagingSpecs = append(m.packagingSpecs, spec)
return spec
@@ -3230,6 +3281,7 @@
srcPath: nil,
symlinkTarget: relPath,
executable: false,
+ partition: fullInstallPath.partition,
})
return fullInstallPath
@@ -3270,6 +3322,7 @@
srcPath: nil,
symlinkTarget: absPath,
executable: false,
+ partition: fullInstallPath.partition,
})
return fullInstallPath
diff --git a/android/module_test.go b/android/module_test.go
index 1dcddf7..77ef146 100644
--- a/android/module_test.go
+++ b/android/module_test.go
@@ -563,6 +563,12 @@
Embedded_prop *string
}
+type StructInSlice struct {
+ G string
+ H bool
+ I []string
+}
+
type propsTestModule struct {
ModuleBase
DefaultableModuleBase
@@ -579,6 +585,8 @@
E *string
}
F *string `blueprint:"mutated"`
+
+ Slice_of_struct []StructInSlice
}
}
@@ -621,7 +629,7 @@
}
`,
expectedProps: []propInfo{
- propInfo{"Name", "string"},
+ propInfo{Name: "Name", Type: "string", Value: "foo"},
},
},
{
@@ -634,10 +642,10 @@
}
`,
expectedProps: []propInfo{
- propInfo{"A", "string"},
- propInfo{"B", "bool"},
- propInfo{"D", "int64"},
- propInfo{"Name", "string"},
+ propInfo{Name: "A", Type: "string", Value: "abc"},
+ propInfo{Name: "B", Type: "bool", Value: "true"},
+ propInfo{Name: "D", Type: "int64", Value: "123"},
+ propInfo{Name: "Name", Type: "string", Value: "foo"},
},
},
{
@@ -650,10 +658,10 @@
`,
expectedProps: []propInfo{
// for non-pointer cannot distinguish between unused and intentionally set to empty
- propInfo{"A", "string"},
- propInfo{"B", "bool"},
- propInfo{"D", "int64"},
- propInfo{"Name", "string"},
+ propInfo{Name: "A", Type: "string", Value: ""},
+ propInfo{Name: "B", Type: "bool", Value: "true"},
+ propInfo{Name: "D", Type: "int64", Value: "123"},
+ propInfo{Name: "Name", Type: "string", Value: "foo"},
},
},
{
@@ -666,8 +674,8 @@
}
`,
expectedProps: []propInfo{
- propInfo{"Nested.E", "string"},
- propInfo{"Name", "string"},
+ propInfo{Name: "Name", Type: "string", Value: "foo"},
+ propInfo{Name: "Nested.E", Type: "string", Value: "abc"},
},
},
{
@@ -682,8 +690,8 @@
}
`,
expectedProps: []propInfo{
- propInfo{"Name", "string"},
- propInfo{"Arch.X86_64.A", "string"},
+ propInfo{Name: "Arch.X86_64.A", Type: "string", Value: "abc"},
+ propInfo{Name: "Name", Type: "string", Value: "foo"},
},
},
{
@@ -694,8 +702,34 @@
}
`,
expectedProps: []propInfo{
- propInfo{"Embedded_prop", "string"},
- propInfo{"Name", "string"},
+ propInfo{Name: "Embedded_prop", Type: "string", Value: "a"},
+ propInfo{Name: "Name", Type: "string", Value: "foo"},
+ },
+ },
+ {
+ desc: "struct slice",
+ bp: `test {
+ name: "foo",
+ slice_of_struct: [
+ {
+ g: "abc",
+ h: false,
+ i: ["baz"],
+ },
+ {
+ g: "def",
+ h: true,
+ i: [],
+ },
+ ]
+ }
+ `,
+ expectedProps: []propInfo{
+ propInfo{Name: "Name", Type: "string", Value: "foo"},
+ propInfo{Name: "Slice_of_struct", Type: "struct slice", Values: []string{
+ `android.StructInSlice{G: abc, H: false, I: [baz]}`,
+ `android.StructInSlice{G: def, H: true, I: []}`,
+ }},
},
},
{
@@ -705,19 +739,20 @@
name: "foo_defaults",
a: "a",
b: true,
+ c: ["default_c"],
embedded_prop:"a",
arch: {
x86_64: {
- a: "a",
+ a: "x86_64 a",
},
},
}
test {
name: "foo",
defaults: ["foo_defaults"],
- c: ["a"],
+ c: ["c"],
nested: {
- e: "d",
+ e: "nested e",
},
target: {
linux: {
@@ -727,15 +762,15 @@
}
`,
expectedProps: []propInfo{
- propInfo{"A", "string"},
- propInfo{"B", "bool"},
- propInfo{"C", "string slice"},
- propInfo{"Embedded_prop", "string"},
- propInfo{"Nested.E", "string"},
- propInfo{"Name", "string"},
- propInfo{"Arch.X86_64.A", "string"},
- propInfo{"Target.Linux.A", "string"},
- propInfo{"Defaults", "string slice"},
+ propInfo{Name: "A", Type: "string", Value: "a"},
+ propInfo{Name: "Arch.X86_64.A", Type: "string", Value: "x86_64 a"},
+ propInfo{Name: "B", Type: "bool", Value: "true"},
+ propInfo{Name: "C", Type: "string slice", Values: []string{"default_c", "c"}},
+ propInfo{Name: "Defaults", Type: "string slice", Values: []string{"foo_defaults"}},
+ propInfo{Name: "Embedded_prop", Type: "string", Value: "a"},
+ propInfo{Name: "Name", Type: "string", Value: "foo"},
+ propInfo{Name: "Nested.E", Type: "string", Value: "nested e"},
+ propInfo{Name: "Target.Linux.A", Type: "string", Value: "a"},
},
},
}
diff --git a/android/packaging.go b/android/packaging.go
index e3a0b54..ecd84a2 100644
--- a/android/packaging.go
+++ b/android/packaging.go
@@ -40,6 +40,8 @@
executable bool
effectiveLicenseFiles *Paths
+
+ partition string
}
// Get file name of installed package
@@ -67,6 +69,10 @@
return *p.effectiveLicenseFiles
}
+func (p *PackagingSpec) Partition() string {
+ return p.partition
+}
+
type PackageModule interface {
Module
packagingBase() *PackagingBase
@@ -76,11 +82,14 @@
// be copied to a zip in CopyDepsToZip, `depTag` should implement PackagingItem marker interface.
AddDeps(ctx BottomUpMutatorContext, depTag blueprint.DependencyTag)
+ // GatherPackagingSpecs gathers PackagingSpecs of transitive dependencies.
+ GatherPackagingSpecs(ctx ModuleContext) map[string]PackagingSpec
+
// CopyDepsToZip zips the built artifacts of the dependencies into the given zip file and
// returns zip entries in it. This is expected to be called in GenerateAndroidBuildActions,
// followed by a build rule that unzips it and creates the final output (img, zip, tar.gz,
// etc.) from the extracted files
- CopyDepsToZip(ctx ModuleContext, zipOut WritablePath) []string
+ CopyDepsToZip(ctx ModuleContext, specs map[string]PackagingSpec, zipOut WritablePath) []string
}
// PackagingBase provides basic functionality for packaging dependencies. A module is expected to
@@ -211,7 +220,7 @@
}
}
-// Returns transitive PackagingSpecs from deps
+// See PackageModule.GatherPackagingSpecs
func (p *PackagingBase) GatherPackagingSpecs(ctx ModuleContext) map[string]PackagingSpec {
m := make(map[string]PackagingSpec)
ctx.VisitDirectDeps(func(child Module) {
@@ -229,10 +238,10 @@
// CopySpecsToDir is a helper that will add commands to the rule builder to copy the PackagingSpec
// entries into the specified directory.
-func (p *PackagingBase) CopySpecsToDir(ctx ModuleContext, builder *RuleBuilder, m map[string]PackagingSpec, dir ModuleOutPath) (entries []string) {
+func (p *PackagingBase) CopySpecsToDir(ctx ModuleContext, builder *RuleBuilder, specs map[string]PackagingSpec, dir ModuleOutPath) (entries []string) {
seenDir := make(map[string]bool)
- for _, k := range SortedStringKeys(m) {
- ps := m[k]
+ for _, k := range SortedStringKeys(specs) {
+ ps := specs[k]
destPath := dir.Join(ctx, ps.relPathInPackage).String()
destDir := filepath.Dir(destPath)
entries = append(entries, ps.relPathInPackage)
@@ -254,14 +263,13 @@
}
// See PackageModule.CopyDepsToZip
-func (p *PackagingBase) CopyDepsToZip(ctx ModuleContext, zipOut WritablePath) (entries []string) {
- m := p.GatherPackagingSpecs(ctx)
+func (p *PackagingBase) CopyDepsToZip(ctx ModuleContext, specs map[string]PackagingSpec, zipOut WritablePath) (entries []string) {
builder := NewRuleBuilder(pctx, ctx)
dir := PathForModuleOut(ctx, ".zip")
builder.Command().Text("rm").Flag("-rf").Text(dir.String())
builder.Command().Text("mkdir").Flag("-p").Text(dir.String())
- entries = p.CopySpecsToDir(ctx, builder, m, dir)
+ entries = p.CopySpecsToDir(ctx, builder, specs, dir)
builder.Command().
BuiltTool("soong_zip").
diff --git a/android/packaging_test.go b/android/packaging_test.go
index ff7446c..91ac1f3 100644
--- a/android/packaging_test.go
+++ b/android/packaging_test.go
@@ -95,7 +95,7 @@
func (m *packageTestModule) GenerateAndroidBuildActions(ctx ModuleContext) {
zipFile := PathForModuleOut(ctx, "myzip.zip")
- m.entries = m.CopyDepsToZip(ctx, zipFile)
+ m.entries = m.CopyDepsToZip(ctx, m.GatherPackagingSpecs(ctx), zipFile)
}
func runPackagingTest(t *testing.T, multitarget bool, bp string, expected []string) {
diff --git a/android/variable.go b/android/variable.go
index 37037eb..4ed0507 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -201,6 +201,7 @@
Platform_base_os *string `json:",omitempty"`
DeviceName *string `json:",omitempty"`
+ DeviceProduct *string `json:",omitempty"`
DeviceArch *string `json:",omitempty"`
DeviceArchVariant *string `json:",omitempty"`
DeviceCpuVariant *string `json:",omitempty"`
@@ -467,6 +468,7 @@
HostArch: stringPtr("x86_64"),
HostSecondaryArch: stringPtr("x86"),
DeviceName: stringPtr("generic_arm64"),
+ DeviceProduct: stringPtr("aosp_arm-eng"),
DeviceArch: stringPtr("arm64"),
DeviceArchVariant: stringPtr("armv8-a"),
DeviceCpuVariant: stringPtr("generic"),
diff --git a/apex/apex.go b/apex/apex.go
index 5631371..6d8a67a 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -1023,6 +1023,9 @@
// Do not traverse transitive deps of libcore/ libs
return false
}
+ if android.InList(child.Name(), skipLintJavalibAllowlist) {
+ return false
+ }
if lintable, ok := child.(java.LintDepSetsIntf); ok {
lintable.SetStrictUpdatabilityLinting(true)
}
@@ -1047,6 +1050,17 @@
"test_com.android.media",
"test_jitzygote_com.android.art",
}
+
+ // TODO: b/215736885 Remove this list
+ skipLintJavalibAllowlist = []string{
+ "conscrypt.module.platform.api.stubs",
+ "conscrypt.module.public.api.stubs",
+ "conscrypt.module.public.api.stubs.system",
+ "conscrypt.module.public.api.stubs.module_lib",
+ "framework-media.stubs",
+ "framework-media.stubs.system",
+ "framework-media.stubs.module_lib",
+ }
)
func (a *apexBundle) checkStrictUpdatabilityLinting() bool {
diff --git a/bp2build/cc_library_conversion_test.go b/bp2build/cc_library_conversion_test.go
index d84e156..5767861 100644
--- a/bp2build/cc_library_conversion_test.go
+++ b/bp2build/cc_library_conversion_test.go
@@ -283,8 +283,8 @@
srcs: ["both.cpp"],
cflags: ["bothflag"],
shared_libs: ["shared_dep_for_both"],
- static_libs: ["static_dep_for_both"],
- whole_static_libs: ["whole_static_lib_for_both"],
+ static_libs: ["static_dep_for_both", "whole_and_static_lib_for_both"],
+ whole_static_libs: ["whole_static_lib_for_both", "whole_and_static_lib_for_both"],
static: {
srcs: ["staticonly.cpp"],
cflags: ["staticflag"],
@@ -332,6 +332,11 @@
bazel_module: { bp2build_available: false },
}
+cc_library_static {
+ name: "whole_and_static_lib_for_both",
+ bazel_module: { bp2build_available: false },
+}
+
cc_library {
name: "shared_dep_for_shared",
bazel_module: { bp2build_available: false },
@@ -367,6 +372,7 @@
]`,
"whole_archive_deps": `[
":whole_static_lib_for_both",
+ ":whole_and_static_lib_for_both",
":whole_static_lib_for_static",
]`}),
makeBazelTarget("cc_library_shared", "a", attrNameToString{
@@ -388,6 +394,7 @@
]`,
"whole_archive_deps": `[
":whole_static_lib_for_both",
+ ":whole_and_static_lib_for_both",
":whole_static_lib_for_shared",
]`,
}),
diff --git a/bp2build/metrics.go b/bp2build/metrics.go
index 8a0b1c9..04fac44 100644
--- a/bp2build/metrics.go
+++ b/bp2build/metrics.go
@@ -43,6 +43,8 @@
// Counts of total modules by module type.
totalModuleTypeCount map[string]uint64
+
+ Events []*bp2build_metrics_proto.Event
}
// Serialize returns the protoized version of CodegenMetrics: bp2build_metrics_proto.Bp2BuildMetrics
@@ -55,6 +57,7 @@
ConvertedModules: metrics.convertedModules,
ConvertedModuleTypeCount: metrics.convertedModuleTypeCount,
TotalModuleTypeCount: metrics.totalModuleTypeCount,
+ Events: metrics.Events,
}
}
diff --git a/cc/bp2build.go b/cc/bp2build.go
index 93d11c7..811e228 100644
--- a/cc/bp2build.go
+++ b/cc/bp2build.go
@@ -585,9 +585,12 @@
// Use a single variable to capture usage of nocrt in arch variants, so there's only 1 error message for this module
var axisFeatures []string
+ wholeStaticLibs := android.FirstUniqueStrings(props.Whole_static_libs)
+ la.wholeArchiveDeps.SetSelectValue(axis, config, bazelLabelForWholeDepsExcludes(ctx, wholeStaticLibs, props.Exclude_static_libs))
// Excludes to parallel Soong:
// https://cs.android.com/android/platform/superproject/+/master:build/soong/cc/linker.go;l=247-249;drc=088b53577dde6e40085ffd737a1ae96ad82fc4b0
- staticLibs := android.FirstUniqueStrings(props.Static_libs)
+ staticLibs := android.FirstUniqueStrings(android.RemoveListFromList(props.Static_libs, wholeStaticLibs))
+
staticDeps := maybePartitionExportedAndImplementationsDepsExcludes(ctx, !isBinary, staticLibs, props.Exclude_static_libs, props.Export_static_lib_headers, bazelLabelForStaticDepsExcludes)
headerLibs := android.FirstUniqueStrings(props.Header_libs)
@@ -599,9 +602,6 @@
(&hDeps.implementation).Append(staticDeps.implementation)
la.implementationDeps.SetSelectValue(axis, config, hDeps.implementation)
- wholeStaticLibs := android.FirstUniqueStrings(props.Whole_static_libs)
- la.wholeArchiveDeps.SetSelectValue(axis, config, bazelLabelForWholeDepsExcludes(ctx, wholeStaticLibs, props.Exclude_static_libs))
-
systemSharedLibs := props.System_shared_libs
// systemSharedLibs distinguishes between nil/empty list behavior:
// nil -> use default values
diff --git a/cc/config/global.go b/cc/config/global.go
index 400be31..8dda537 100644
--- a/cc/config/global.go
+++ b/cc/config/global.go
@@ -287,8 +287,8 @@
// prebuilts/clang default settings.
ClangDefaultBase = "prebuilts/clang/host"
- ClangDefaultVersion = "clang-r445002"
- ClangDefaultShortVersion = "14.0.2"
+ ClangDefaultVersion = "clang-r450784"
+ ClangDefaultShortVersion = "14.0.3"
// Directories with warnings from Android.bp files.
WarningAllowedProjects = []string{
diff --git a/cc/config/x86_linux_host.go b/cc/config/x86_linux_host.go
index ce6836b..e1659d3 100644
--- a/cc/config/x86_linux_host.go
+++ b/cc/config/x86_linux_host.go
@@ -65,7 +65,6 @@
linuxMuslLdflags = []string{
"-nostdlib",
- "-lgcc", "-lgcc_eh",
"--sysroot /dev/null",
}
diff --git a/cc/sanitize.go b/cc/sanitize.go
index f8661a6..814fef6 100644
--- a/cc/sanitize.go
+++ b/cc/sanitize.go
@@ -76,7 +76,7 @@
minimalRuntimeFlags = []string{"-fsanitize-minimal-runtime", "-fno-sanitize-trap=integer,undefined",
"-fno-sanitize-recover=integer,undefined"}
hwasanGlobalOptions = []string{"heap_history_size=1023", "stack_history_size=512",
- "export_memory_stats=0", "max_malloc_fill_size=0"}
+ "export_memory_stats=0", "max_malloc_fill_size=4096", "malloc_fill_byte=0"}
)
type SanitizerType int
@@ -480,8 +480,8 @@
s.Diag.Cfi = nil
}
- // Disable sanitizers that depend on the UBSan runtime for windows/darwin/musl builds.
- if !ctx.Os().Linux() || ctx.Os() == android.LinuxMusl {
+ // Disable sanitizers that depend on the UBSan runtime for windows/darwin builds.
+ if !ctx.Os().Linux() {
s.Cfi = nil
s.Diag.Cfi = nil
s.Misc_undefined = nil
@@ -490,6 +490,12 @@
s.Integer_overflow = nil
}
+ // Disable CFI for musl
+ if ctx.toolchain().Musl() {
+ s.Cfi = nil
+ s.Diag.Cfi = nil
+ }
+
// Also disable CFI for VNDK variants of components
if ctx.isVndk() && ctx.useVndk() {
if ctx.static() {
@@ -702,10 +708,10 @@
flags.Local.AsFlags = append(flags.Local.AsFlags, sanitizeArg)
flags.Local.LdFlags = append(flags.Local.LdFlags, sanitizeArg)
- if ctx.toolchain().Bionic() {
- // Bionic sanitizer runtimes have already been added as dependencies so that
- // the right variant of the runtime will be used (with the "-android"
- // suffix), so don't let clang the runtime library.
+ if ctx.toolchain().Bionic() || ctx.toolchain().Musl() {
+ // Bionic and musl sanitizer runtimes have already been added as dependencies so that
+ // the right variant of the runtime will be used (with the "-android" or "-musl"
+ // suffixes), so don't let clang the runtime library.
flags.Local.LdFlags = append(flags.Local.LdFlags, "-fno-sanitize-link-runtime")
} else {
// Host sanitizers only link symbols in the final executable, so
@@ -1217,7 +1223,7 @@
addStaticDeps(config.BuiltinsRuntimeLibrary(toolchain))
}
- if runtimeLibrary != "" && (toolchain.Bionic() || c.sanitize.Properties.UbsanRuntimeDep) {
+ if runtimeLibrary != "" && (toolchain.Bionic() || toolchain.Musl() || c.sanitize.Properties.UbsanRuntimeDep) {
// UBSan is supported on non-bionic linux host builds as well
// Adding dependency to the runtime library. We are using *FarVariation*
diff --git a/cmd/soong_build/main.go b/cmd/soong_build/main.go
index b3a6ee0..4b3161b 100644
--- a/cmd/soong_build/main.go
+++ b/cmd/soong_build/main.go
@@ -26,10 +26,11 @@
"android/soong/android"
"android/soong/bp2build"
"android/soong/shared"
+ "android/soong/ui/metrics/bp2build_metrics_proto"
"github.com/google/blueprint/bootstrap"
"github.com/google/blueprint/deptools"
- "github.com/google/blueprint/pathtools"
+ "github.com/google/blueprint/metrics"
androidProtobuf "google.golang.org/protobuf/android"
)
@@ -134,8 +135,14 @@
// TODO(cparsons): Don't output any ninja file, as the second pass will overwrite
// the incorrect results from the first pass, and file I/O is expensive.
func runMixedModeBuild(configuration android.Config, firstCtx *android.Context, extraNinjaDeps []string) {
- bootstrap.RunBlueprint(cmdlineArgs, bootstrap.StopBeforeWriteNinja, firstCtx.Context, configuration)
+ firstCtx.EventHandler.Begin("mixed_build")
+ defer firstCtx.EventHandler.End("mixed_build")
+ firstCtx.EventHandler.Begin("prepare")
+ bootstrap.RunBlueprint(cmdlineArgs, bootstrap.StopBeforeWriteNinja, firstCtx.Context, configuration)
+ firstCtx.EventHandler.End("prepare")
+
+ firstCtx.EventHandler.Begin("bazel")
// Invoke bazel commands and save results for second pass.
if err := configuration.BazelContext.InvokeBazel(); err != nil {
fmt.Fprintf(os.Stderr, "%s", err)
@@ -147,18 +154,25 @@
fmt.Fprintf(os.Stderr, "%s", err)
os.Exit(1)
}
+ firstCtx.EventHandler.End("bazel")
+
secondCtx := newContext(secondConfig)
+ secondCtx.EventHandler = firstCtx.EventHandler
+ secondCtx.EventHandler.Begin("analyze")
ninjaDeps := bootstrap.RunBlueprint(cmdlineArgs, bootstrap.DoEverything, secondCtx.Context, secondConfig)
ninjaDeps = append(ninjaDeps, extraNinjaDeps...)
+ secondCtx.EventHandler.End("analyze")
- globListFiles := writeBuildGlobsNinjaFile(secondCtx.SrcDir(), configuration.SoongOutDir(), secondCtx.Globs, configuration)
+ globListFiles := writeBuildGlobsNinjaFile(secondCtx, configuration.SoongOutDir(), configuration)
ninjaDeps = append(ninjaDeps, globListFiles...)
- writeDepFile(cmdlineArgs.OutFile, ninjaDeps)
+ writeDepFile(cmdlineArgs.OutFile, *secondCtx.EventHandler, ninjaDeps)
}
// Run the code-generation phase to convert BazelTargetModules to BUILD files.
func runQueryView(queryviewDir, queryviewMarker string, configuration android.Config, ctx *android.Context) {
+ ctx.EventHandler.Begin("queryview")
+ defer ctx.EventHandler.End("queryview")
codegenContext := bp2build.NewCodegenContext(configuration, *ctx, bp2build.QueryView)
absoluteQueryViewDir := shared.JoinPath(topDir, queryviewDir)
if err := createBazelQueryView(codegenContext, absoluteQueryViewDir); err != nil {
@@ -169,9 +183,14 @@
touch(shared.JoinPath(topDir, queryviewMarker))
}
-func writeMetrics(configuration android.Config) {
- metricsFile := filepath.Join(configuration.SoongOutDir(), "soong_build_metrics.pb")
- err := android.WriteMetrics(configuration, metricsFile)
+func writeMetrics(configuration android.Config, eventHandler metrics.EventHandler) {
+ metricsDir := configuration.Getenv("LOG_DIR")
+ if len(metricsDir) < 1 {
+ fmt.Fprintf(os.Stderr, "\nMissing required env var for generating soong metrics: LOG_DIR\n")
+ os.Exit(1)
+ }
+ metricsFile := filepath.Join(metricsDir, "soong_build_metrics.pb")
+ err := android.WriteMetrics(configuration, eventHandler, metricsFile)
if err != nil {
fmt.Fprintf(os.Stderr, "error writing soong_build metrics %s: %s", metricsFile, err)
os.Exit(1)
@@ -191,18 +210,23 @@
ctx.Context.PrintJSONGraphAndActions(graphFile, actionsFile)
}
-func writeBuildGlobsNinjaFile(srcDir, buildDir string, globs func() pathtools.MultipleGlobResults, config interface{}) []string {
+func writeBuildGlobsNinjaFile(ctx *android.Context, buildDir string, config interface{}) []string {
+ ctx.EventHandler.Begin("globs_ninja_file")
+ defer ctx.EventHandler.End("globs_ninja_file")
+
globDir := bootstrap.GlobDirectory(buildDir, globListDir)
bootstrap.WriteBuildGlobsNinjaFile(&bootstrap.GlobSingleton{
- GlobLister: globs,
+ GlobLister: ctx.Globs,
GlobFile: globFile,
GlobDir: globDir,
- SrcDir: srcDir,
+ SrcDir: ctx.SrcDir(),
}, config)
return bootstrap.GlobFileListFiles(globDir)
}
-func writeDepFile(outputFile string, ninjaDeps []string) {
+func writeDepFile(outputFile string, eventHandler metrics.EventHandler, ninjaDeps []string) {
+ eventHandler.Begin("ninja_deps")
+ defer eventHandler.End("ninja_deps")
depFile := shared.JoinPath(topDir, outputFile+".d")
err := deptools.WriteDepFile(depFile, outputFile, ninjaDeps)
if err != nil {
@@ -230,36 +254,36 @@
blueprintArgs := cmdlineArgs
- var stopBefore bootstrap.StopBefore
- if generateModuleGraphFile {
- stopBefore = bootstrap.StopBeforeWriteNinja
- } else if generateQueryView {
- stopBefore = bootstrap.StopBeforePrepareBuildActions
- } else if generateDocFile {
- stopBefore = bootstrap.StopBeforePrepareBuildActions
- } else {
- stopBefore = bootstrap.DoEverything
- }
-
ctx := newContext(configuration)
if mixedModeBuild {
runMixedModeBuild(configuration, ctx, extraNinjaDeps)
} else {
+ var stopBefore bootstrap.StopBefore
+ if generateModuleGraphFile {
+ stopBefore = bootstrap.StopBeforeWriteNinja
+ } else if generateQueryView {
+ stopBefore = bootstrap.StopBeforePrepareBuildActions
+ } else if generateDocFile {
+ stopBefore = bootstrap.StopBeforePrepareBuildActions
+ } else {
+ stopBefore = bootstrap.DoEverything
+ }
+
ninjaDeps := bootstrap.RunBlueprint(blueprintArgs, stopBefore, ctx.Context, configuration)
ninjaDeps = append(ninjaDeps, extraNinjaDeps...)
- globListFiles := writeBuildGlobsNinjaFile(ctx.SrcDir(), configuration.SoongOutDir(), ctx.Globs, configuration)
+ globListFiles := writeBuildGlobsNinjaFile(ctx, configuration.SoongOutDir(), configuration)
ninjaDeps = append(ninjaDeps, globListFiles...)
// Convert the Soong module graph into Bazel BUILD files.
if generateQueryView {
queryviewMarkerFile := bazelQueryViewDir + ".marker"
runQueryView(bazelQueryViewDir, queryviewMarkerFile, configuration, ctx)
- writeDepFile(queryviewMarkerFile, ninjaDeps)
+ writeDepFile(queryviewMarkerFile, *ctx.EventHandler, ninjaDeps)
return queryviewMarkerFile
} else if generateModuleGraphFile {
writeJsonModuleGraphAndActions(ctx, moduleGraphFile, moduleActionsFile)
- writeDepFile(moduleGraphFile, ninjaDeps)
+ writeDepFile(moduleGraphFile, *ctx.EventHandler, ninjaDeps)
return moduleGraphFile
} else if generateDocFile {
// TODO: we could make writeDocs() return the list of documentation files
@@ -269,16 +293,16 @@
fmt.Fprintf(os.Stderr, "error building Soong documentation: %s\n", err)
os.Exit(1)
}
- writeDepFile(docFile, ninjaDeps)
+ writeDepFile(docFile, *ctx.EventHandler, ninjaDeps)
return docFile
} else {
// The actual output (build.ninja) was written in the RunBlueprint() call
// above
- writeDepFile(cmdlineArgs.OutFile, ninjaDeps)
+ writeDepFile(cmdlineArgs.OutFile, *ctx.EventHandler, ninjaDeps)
}
}
- writeMetrics(configuration)
+ writeMetrics(configuration, *ctx.EventHandler)
return cmdlineArgs.OutFile
}
@@ -335,6 +359,7 @@
}
finalOutputFile := doChosenActivity(configuration, extraNinjaDeps)
+
writeUsedEnvironmentFile(configuration, finalOutputFile)
}
@@ -466,6 +491,9 @@
// an alternate pipeline of mutators and singletons specifically for generating
// Bazel BUILD files instead of Ninja files.
func runBp2Build(configuration android.Config, extraNinjaDeps []string) {
+ eventHandler := metrics.EventHandler{}
+ eventHandler.Begin("bp2build")
+
// Register an alternate set of singletons and mutators for bazel
// conversion for Bazel conversion.
bp2buildCtx := android.NewContext(configuration)
@@ -500,7 +528,7 @@
ninjaDeps := bootstrap.RunBlueprint(blueprintArgs, bootstrap.StopBeforePrepareBuildActions, bp2buildCtx.Context, configuration)
ninjaDeps = append(ninjaDeps, extraNinjaDeps...)
- globListFiles := writeBuildGlobsNinjaFile(bp2buildCtx.SrcDir(), configuration.SoongOutDir(), bp2buildCtx.Globs, configuration)
+ globListFiles := writeBuildGlobsNinjaFile(bp2buildCtx, configuration.SoongOutDir(), configuration)
ninjaDeps = append(ninjaDeps, globListFiles...)
// Run the code-generation phase to convert BazelTargetModules to BUILD files
@@ -537,27 +565,38 @@
symlinkForestDeps := bp2build.PlantSymlinkForest(
topDir, workspaceRoot, generatedRoot, ".", excludes)
+ ninjaDeps = append(ninjaDeps, codegenContext.AdditionalNinjaDeps()...)
+ ninjaDeps = append(ninjaDeps, symlinkForestDeps...)
+
+ writeDepFile(bp2buildMarker, eventHandler, ninjaDeps)
+
+ // Create an empty bp2build marker file.
+ touch(shared.JoinPath(topDir, bp2buildMarker))
+
+ eventHandler.End("bp2build")
+
// Only report metrics when in bp2build mode. The metrics aren't relevant
// for queryview, since that's a total repo-wide conversion and there's a
// 1:1 mapping for each module.
metrics.Print()
- writeBp2BuildMetrics(&metrics, configuration)
-
- ninjaDeps = append(ninjaDeps, codegenContext.AdditionalNinjaDeps()...)
- ninjaDeps = append(ninjaDeps, symlinkForestDeps...)
-
- writeDepFile(bp2buildMarker, ninjaDeps)
-
- // Create an empty bp2build marker file.
- touch(shared.JoinPath(topDir, bp2buildMarker))
+ writeBp2BuildMetrics(&metrics, configuration, eventHandler)
}
// Write Bp2Build metrics into $LOG_DIR
-func writeBp2BuildMetrics(metrics *bp2build.CodegenMetrics, configuration android.Config) {
+func writeBp2BuildMetrics(codegenMetrics *bp2build.CodegenMetrics,
+ configuration android.Config, eventHandler metrics.EventHandler) {
+ for _, event := range eventHandler.CompletedEvents() {
+ codegenMetrics.Events = append(codegenMetrics.Events,
+ &bp2build_metrics_proto.Event{
+ Name: event.Id,
+ StartTime: uint64(event.Start.UnixNano()),
+ RealTime: event.RuntimeNanoseconds(),
+ })
+ }
metricsDir := configuration.Getenv("LOG_DIR")
if len(metricsDir) < 1 {
fmt.Fprintf(os.Stderr, "\nMissing required env var for generating bp2build metrics: LOG_DIR\n")
os.Exit(1)
}
- metrics.Write(metricsDir)
+ codegenMetrics.Write(metricsDir)
}
diff --git a/filesystem/filesystem.go b/filesystem/filesystem.go
index 0796258..ccf9e9d 100644
--- a/filesystem/filesystem.go
+++ b/filesystem/filesystem.go
@@ -43,8 +43,14 @@
// Function that builds extra files under the root directory and returns the files
buildExtraFiles func(ctx android.ModuleContext, root android.OutputPath) android.OutputPaths
+ // Function that filters PackagingSpecs returned by PackagingBase.GatherPackagingSpecs()
+ filterPackagingSpecs func(specs map[string]android.PackagingSpec)
+
output android.OutputPath
installDir android.InstallPath
+
+ // For testing. Keeps the result of CopyDepsToZip()
+ entries []string
}
type symlinkDefinition struct {
@@ -226,7 +232,7 @@
func (f *filesystem) buildImageUsingBuildImage(ctx android.ModuleContext) android.OutputPath {
depsZipFile := android.PathForModuleOut(ctx, "deps.zip").OutputPath
- f.CopyDepsToZip(ctx, depsZipFile)
+ f.entries = f.CopyDepsToZip(ctx, f.gatherFilteredPackagingSpecs(ctx), depsZipFile)
builder := android.NewRuleBuilder(pctx, ctx)
depsBase := proptools.StringDefault(f.properties.Base_dir, ".")
@@ -345,7 +351,7 @@
}
depsZipFile := android.PathForModuleOut(ctx, "deps.zip").OutputPath
- f.CopyDepsToZip(ctx, depsZipFile)
+ f.entries = f.CopyDepsToZip(ctx, f.gatherFilteredPackagingSpecs(ctx), depsZipFile)
builder := android.NewRuleBuilder(pctx, ctx)
depsBase := proptools.StringDefault(f.properties.Base_dir, ".")
@@ -434,3 +440,14 @@
}
return nil
}
+
+// Filter the result of GatherPackagingSpecs to discard items targeting outside "system" partition.
+// Note that "apex" module installs its contents to "apex"(fake partition) as well
+// for symbol lookup by imitating "activated" paths.
+func (f *filesystem) gatherFilteredPackagingSpecs(ctx android.ModuleContext) map[string]android.PackagingSpec {
+ specs := f.PackagingBase.GatherPackagingSpecs(ctx)
+ if f.filterPackagingSpecs != nil {
+ f.filterPackagingSpecs(specs)
+ }
+ return specs
+}
diff --git a/filesystem/filesystem_test.go b/filesystem/filesystem_test.go
index e78fdff..cda06d9 100644
--- a/filesystem/filesystem_test.go
+++ b/filesystem/filesystem_test.go
@@ -45,11 +45,11 @@
func TestFileSystemFillsLinkerConfigWithStubLibs(t *testing.T) {
result := fixture.RunTestWithBp(t, `
- android_system_image {
+ android_system_image {
name: "myfilesystem",
deps: [
"libfoo",
- "libbar",
+ "libbar",
],
linker_config_src: "linker.config.json",
}
@@ -74,3 +74,54 @@
android.AssertStringDoesNotContain(t, "linker.config.pb should not have libbar",
output.RuleParams.Command, "libbar.so")
}
+
+func registerComponent(ctx android.RegistrationContext) {
+ ctx.RegisterModuleType("component", componentFactory)
+}
+
+func componentFactory() android.Module {
+ m := &component{}
+ m.AddProperties(&m.properties)
+ android.InitAndroidArchModule(m, android.DeviceSupported, android.MultilibCommon)
+ return m
+}
+
+type component struct {
+ android.ModuleBase
+ properties struct {
+ Install_copy_in_data []string
+ }
+}
+
+func (c *component) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ output := android.PathForModuleOut(ctx, c.Name())
+ dir := android.PathForModuleInstall(ctx, "components")
+ ctx.InstallFile(dir, c.Name(), output)
+
+ dataDir := android.PathForModuleInPartitionInstall(ctx, "data", "components")
+ for _, d := range c.properties.Install_copy_in_data {
+ ctx.InstallFile(dataDir, d, output)
+ }
+}
+
+func TestFileSystemGathersItemsOnlyInSystemPartition(t *testing.T) {
+ f := android.GroupFixturePreparers(fixture, android.FixtureRegisterWithContext(registerComponent))
+ result := f.RunTestWithBp(t, `
+ android_system_image {
+ name: "myfilesystem",
+ multilib: {
+ common: {
+ deps: ["foo"],
+ },
+ },
+ linker_config_src: "linker.config.json",
+ }
+ component {
+ name: "foo",
+ install_copy_in_data: ["bar"],
+ }
+ `)
+
+ module := result.ModuleForTests("myfilesystem", "android_common").Module().(*systemImage)
+ android.AssertDeepEquals(t, "entries should have foo only", []string{"components/foo"}, module.entries)
+}
diff --git a/filesystem/system_image.go b/filesystem/system_image.go
index 1d24d6d..75abf70 100644
--- a/filesystem/system_image.go
+++ b/filesystem/system_image.go
@@ -37,6 +37,7 @@
module := &systemImage{}
module.AddProperties(&module.properties)
module.filesystem.buildExtraFiles = module.buildExtraFiles
+ module.filesystem.filterPackagingSpecs = module.filterPackagingSpecs
initFilesystemModule(&module.filesystem)
return module
}
@@ -53,7 +54,7 @@
// we need "Module"s for packaging items
var otherModules []android.Module
- deps := s.GatherPackagingSpecs(ctx)
+ deps := s.gatherFilteredPackagingSpecs(ctx)
ctx.WalkDeps(func(child, parent android.Module) bool {
for _, ps := range child.PackagingSpecs() {
if _, ok := deps[ps.RelPathInPackage()]; ok {
@@ -68,3 +69,14 @@
builder.Build("conv_linker_config", "Generate linker config protobuf "+output.String())
return output
}
+
+// Filter the result of GatherPackagingSpecs to discard items targeting outside "system" partition.
+// Note that "apex" module installs its contents to "apex"(fake partition) as well
+// for symbol lookup by imitating "activated" paths.
+func (s *systemImage) filterPackagingSpecs(specs map[string]android.PackagingSpec) {
+ for k, ps := range specs {
+ if ps.Partition() != "system" {
+ delete(specs, k)
+ }
+ }
+}
diff --git a/java/builder.go b/java/builder.go
index c48e3fa..c0fadd4 100644
--- a/java/builder.go
+++ b/java/builder.go
@@ -131,31 +131,28 @@
turbine, turbineRE = pctx.RemoteStaticRules("turbine",
blueprint.RuleParams{
- Command: `rm -rf "$outDir" && mkdir -p "$outDir" && ` +
- `$reTemplate${config.JavaCmd} ${config.JavaVmFlags} -jar ${config.TurbineJar} --output $out.tmp ` +
- `--temp_dir "$outDir" --sources @$out.rsp --source_jars $srcJars ` +
+ Command: `$reTemplate${config.JavaCmd} ${config.JavaVmFlags} -jar ${config.TurbineJar} $outputFlags ` +
+ `--sources @$out.rsp --source_jars $srcJars ` +
`--javacopts ${config.CommonJdkFlags} ` +
- `$javacFlags -source $javaVersion -target $javaVersion -- $bootClasspath $classpath && ` +
- `${config.Ziptime} $out.tmp && ` +
- `(if cmp -s $out.tmp $out ; then rm $out.tmp ; else mv $out.tmp $out ; fi )`,
+ `$javacFlags -source $javaVersion -target $javaVersion -- $turbineFlags && ` +
+ `(for o in $outputs; do if cmp -s $${o}.tmp $${o} ; then rm $${o}.tmp ; else mv $${o}.tmp $${o} ; fi; done )`,
CommandDeps: []string{
"${config.TurbineJar}",
"${config.JavaCmd}",
- "${config.Ziptime}",
},
Rspfile: "$out.rsp",
RspfileContent: "$in",
Restat: true,
},
&remoteexec.REParams{Labels: map[string]string{"type": "tool", "name": "turbine"},
- ExecStrategy: "${config.RETurbineExecStrategy}",
- Inputs: []string{"${config.TurbineJar}", "${out}.rsp", "$implicits"},
- RSPFiles: []string{"${out}.rsp"},
- OutputFiles: []string{"$out.tmp"},
- OutputDirectories: []string{"$outDir"},
- ToolchainInputs: []string{"${config.JavaCmd}"},
- Platform: map[string]string{remoteexec.PoolKey: "${config.REJavaPool}"},
- }, []string{"javacFlags", "bootClasspath", "classpath", "srcJars", "outDir", "javaVersion"}, []string{"implicits"})
+ ExecStrategy: "${config.RETurbineExecStrategy}",
+ Inputs: []string{"${config.TurbineJar}", "${out}.rsp", "$implicits"},
+ RSPFiles: []string{"${out}.rsp"},
+ OutputFiles: []string{"$rbeOutputs"},
+ ToolchainInputs: []string{"${config.JavaCmd}"},
+ Platform: map[string]string{remoteexec.PoolKey: "${config.REJavaPool}"},
+ },
+ []string{"javacFlags", "turbineFlags", "outputFlags", "javaVersion", "outputs", "rbeOutputs", "srcJars"}, []string{"implicits"})
jar, jarRE = pctx.RemoteStaticRules("jar",
blueprint.RuleParams{
@@ -358,11 +355,8 @@
})
}
-func TransformJavaToHeaderClasses(ctx android.ModuleContext, outputFile android.WritablePath,
- srcFiles, srcJars android.Paths, flags javaBuilderFlags) {
-
+func turbineFlags(ctx android.ModuleContext, flags javaBuilderFlags) (string, android.Paths) {
var deps android.Paths
- deps = append(deps, srcJars...)
classpath := flags.classpath
@@ -384,20 +378,31 @@
}
deps = append(deps, classpath...)
- deps = append(deps, flags.processorPath...)
+ turbineFlags := bootClasspath + " " + classpath.FormTurbineClassPath("--classpath ")
+
+ return turbineFlags, deps
+}
+
+func TransformJavaToHeaderClasses(ctx android.ModuleContext, outputFile android.WritablePath,
+ srcFiles, srcJars android.Paths, flags javaBuilderFlags) {
+
+ turbineFlags, deps := turbineFlags(ctx, flags)
+
+ deps = append(deps, srcJars...)
rule := turbine
args := map[string]string{
- "javacFlags": flags.javacFlags,
- "bootClasspath": bootClasspath,
- "srcJars": strings.Join(srcJars.Strings(), " "),
- "classpath": classpath.FormTurbineClassPath("--classpath "),
- "outDir": android.PathForModuleOut(ctx, "turbine", "classes").String(),
- "javaVersion": flags.javaVersion.String(),
+ "javacFlags": flags.javacFlags,
+ "srcJars": strings.Join(srcJars.Strings(), " "),
+ "javaVersion": flags.javaVersion.String(),
+ "turbineFlags": turbineFlags,
+ "outputFlags": "--output " + outputFile.String() + ".tmp",
+ "outputs": outputFile.String(),
}
if ctx.Config().UseRBE() && ctx.Config().IsEnvTrue("RBE_TURBINE") {
rule = turbineRE
args["implicits"] = strings.Join(deps.Strings(), ",")
+ args["rbeOutputs"] = outputFile.String() + ".tmp"
}
ctx.Build(pctx, android.BuildParams{
Rule: rule,
@@ -409,6 +414,47 @@
})
}
+// TurbineApt produces a rule to run annotation processors using turbine.
+func TurbineApt(ctx android.ModuleContext, outputSrcJar, outputResJar android.WritablePath,
+ srcFiles, srcJars android.Paths, flags javaBuilderFlags) {
+
+ turbineFlags, deps := turbineFlags(ctx, flags)
+
+ deps = append(deps, srcJars...)
+
+ deps = append(deps, flags.processorPath...)
+ turbineFlags += " " + flags.processorPath.FormTurbineClassPath("--processorpath ")
+ turbineFlags += " --processors " + strings.Join(flags.processors, " ")
+
+ outputs := android.WritablePaths{outputSrcJar, outputResJar}
+ outputFlags := "--gensrc_output " + outputSrcJar.String() + ".tmp " +
+ "--resource_output " + outputResJar.String() + ".tmp"
+
+ rule := turbine
+ args := map[string]string{
+ "javacFlags": flags.javacFlags,
+ "srcJars": strings.Join(srcJars.Strings(), " "),
+ "javaVersion": flags.javaVersion.String(),
+ "turbineFlags": turbineFlags,
+ "outputFlags": outputFlags,
+ "outputs": strings.Join(outputs.Strings(), " "),
+ }
+ if ctx.Config().UseRBE() && ctx.Config().IsEnvTrue("RBE_TURBINE") {
+ rule = turbineRE
+ args["implicits"] = strings.Join(deps.Strings(), ",")
+ args["rbeOutputs"] = outputSrcJar.String() + ".tmp," + outputResJar.String() + ".tmp"
+ }
+ ctx.Build(pctx, android.BuildParams{
+ Rule: rule,
+ Description: "turbine apt",
+ Output: outputs[0],
+ ImplicitOutputs: outputs[1:],
+ Inputs: srcFiles,
+ Implicits: deps,
+ Args: args,
+ })
+}
+
// transformJavaToClasses takes source files and converts them to a jar containing .class files.
// srcFiles is a list of paths to sources, srcJars is a list of paths to jar files that contain
// sources. flags contains various command line flags to be passed to the compiler.
@@ -670,6 +716,6 @@
} else if forceEmpty {
return `--bootclasspath ""`, nil
} else {
- return "", nil
+ return "--system ${config.JavaHome}", nil
}
}
diff --git a/java/java_test.go b/java/java_test.go
index f095c5e..4c93824 100644
--- a/java/java_test.go
+++ b/java/java_test.go
@@ -973,7 +973,7 @@
fooHeaderJar := filepath.Join("out", "soong", ".intermediates", "foo", "android_common", "turbine-combined", "foo.jar")
barTurbineJar := filepath.Join("out", "soong", ".intermediates", "bar", "android_common", "turbine", "bar.jar")
- android.AssertStringDoesContain(t, "bar turbine classpath", barTurbine.Args["classpath"], fooHeaderJar)
+ android.AssertStringDoesContain(t, "bar turbine classpath", barTurbine.Args["turbineFlags"], fooHeaderJar)
android.AssertStringDoesContain(t, "bar javac classpath", barJavac.Args["classpath"], fooHeaderJar)
android.AssertPathsRelativeToTopEquals(t, "bar turbine combineJar", []string{barTurbineJar, fooHeaderJar}, barTurbineCombined.Inputs)
android.AssertStringDoesContain(t, "baz javac classpath", bazJavac.Args["classpath"], "prebuilts/sdk/14/public/android.jar")
diff --git a/java/kotlin.go b/java/kotlin.go
index 3e5cec0..ce79bae 100644
--- a/java/kotlin.go
+++ b/java/kotlin.go
@@ -118,7 +118,7 @@
})
}
-var kapt = pctx.AndroidRemoteStaticRule("kapt", android.RemoteRuleSupports{Goma: true},
+var kaptStubs = pctx.AndroidRemoteStaticRule("kaptStubs", android.RemoteRuleSupports{Goma: true},
blueprint.RuleParams{
Command: `rm -rf "$srcJarDir" "$kotlinBuildFile" "$kaptDir" && ` +
`mkdir -p "$srcJarDir" "$kaptDir/sources" "$kaptDir/classes" && ` +
@@ -133,13 +133,12 @@
`-P plugin:org.jetbrains.kotlin.kapt3:classes=$kaptDir/classes ` +
`-P plugin:org.jetbrains.kotlin.kapt3:stubs=$kaptDir/stubs ` +
`-P plugin:org.jetbrains.kotlin.kapt3:correctErrorTypes=true ` +
- `-P plugin:org.jetbrains.kotlin.kapt3:aptMode=stubsAndApt ` +
+ `-P plugin:org.jetbrains.kotlin.kapt3:aptMode=stubs ` +
`-P plugin:org.jetbrains.kotlin.kapt3:javacArguments=$encodedJavacFlags ` +
`$kaptProcessorPath ` +
`$kaptProcessor ` +
`-Xbuild-file=$kotlinBuildFile && ` +
- `${config.SoongZipCmd} -jar -o $out -C $kaptDir/sources -D $kaptDir/sources && ` +
- `${config.SoongZipCmd} -jar -o $classesJarOut -C $kaptDir/classes -D $kaptDir/classes && ` +
+ `${config.SoongZipCmd} -jar -o $out -C $kaptDir/stubs -D $kaptDir/stubs && ` +
`rm -rf "$srcJarDir"`,
CommandDeps: []string{
"${config.KotlincCmd}",
@@ -197,13 +196,14 @@
kotlinName := filepath.Join(ctx.ModuleDir(), ctx.ModuleSubDir(), ctx.ModuleName())
kotlinName = strings.ReplaceAll(kotlinName, "/", "__")
+ // First run kapt to generate .java stubs from .kt files
+ kaptStubsJar := android.PathForModuleOut(ctx, "kapt", "stubs.jar")
ctx.Build(pctx, android.BuildParams{
- Rule: kapt,
- Description: "kapt",
- Output: srcJarOutputFile,
- ImplicitOutput: resJarOutputFile,
- Inputs: srcFiles,
- Implicits: deps,
+ Rule: kaptStubs,
+ Description: "kapt stubs",
+ Output: kaptStubsJar,
+ Inputs: srcFiles,
+ Implicits: deps,
Args: map[string]string{
"classpath": flags.kotlincClasspath.FormJavaClassPath(""),
"kotlincFlags": flags.kotlincFlags,
@@ -219,6 +219,11 @@
"classesJarOut": resJarOutputFile.String(),
},
})
+
+ // Then run turbine to perform annotation processing on the stubs and any .java srcFiles.
+ javaSrcFiles := srcFiles.FilterByExt(".java")
+ turbineSrcJars := append(android.Paths{kaptStubsJar}, srcJars...)
+ TurbineApt(ctx, srcJarOutputFile, resJarOutputFile, javaSrcFiles, turbineSrcJars, flags)
}
// kapt converts a list of key, value pairs into a base64 encoded Java serialization, which is what kapt expects.
diff --git a/java/kotlin_test.go b/java/kotlin_test.go
index cac0af3..d51bc04 100644
--- a/java/kotlin_test.go
+++ b/java/kotlin_test.go
@@ -117,51 +117,71 @@
buildOS := ctx.Config().BuildOS.String()
- kapt := ctx.ModuleForTests("foo", "android_common").Rule("kapt")
- kotlinc := ctx.ModuleForTests("foo", "android_common").Rule("kotlinc")
- javac := ctx.ModuleForTests("foo", "android_common").Rule("javac")
+ foo := ctx.ModuleForTests("foo", "android_common")
+ kaptStubs := foo.Rule("kapt")
+ turbineApt := foo.Description("turbine apt")
+ kotlinc := foo.Rule("kotlinc")
+ javac := foo.Rule("javac")
bar := ctx.ModuleForTests("bar", buildOS+"_common").Rule("javac").Output.String()
baz := ctx.ModuleForTests("baz", buildOS+"_common").Rule("javac").Output.String()
// Test that the kotlin and java sources are passed to kapt and kotlinc
- if len(kapt.Inputs) != 2 || kapt.Inputs[0].String() != "a.java" || kapt.Inputs[1].String() != "b.kt" {
- t.Errorf(`foo kapt inputs %v != ["a.java", "b.kt"]`, kapt.Inputs)
+ if len(kaptStubs.Inputs) != 2 || kaptStubs.Inputs[0].String() != "a.java" || kaptStubs.Inputs[1].String() != "b.kt" {
+ t.Errorf(`foo kapt inputs %v != ["a.java", "b.kt"]`, kaptStubs.Inputs)
}
if len(kotlinc.Inputs) != 2 || kotlinc.Inputs[0].String() != "a.java" || kotlinc.Inputs[1].String() != "b.kt" {
t.Errorf(`foo kotlinc inputs %v != ["a.java", "b.kt"]`, kotlinc.Inputs)
}
- // Test that only the java sources are passed to javac
+ // Test that only the java sources are passed to turbine-apt and javac
+ if len(turbineApt.Inputs) != 1 || turbineApt.Inputs[0].String() != "a.java" {
+ t.Errorf(`foo turbine apt inputs %v != ["a.java"]`, turbineApt.Inputs)
+ }
if len(javac.Inputs) != 1 || javac.Inputs[0].String() != "a.java" {
t.Errorf(`foo inputs %v != ["a.java"]`, javac.Inputs)
}
- // Test that the kapt srcjar is a dependency of kotlinc and javac rules
- if !inList(kapt.Output.String(), kotlinc.Implicits.Strings()) {
- t.Errorf("expected %q in kotlinc implicits %v", kapt.Output.String(), kotlinc.Implicits.Strings())
- }
- if !inList(kapt.Output.String(), javac.Implicits.Strings()) {
- t.Errorf("expected %q in javac implicits %v", kapt.Output.String(), javac.Implicits.Strings())
+ // Test that the kapt stubs jar is a dependency of turbine-apt
+ if !inList(kaptStubs.Output.String(), turbineApt.Implicits.Strings()) {
+ t.Errorf("expected %q in turbine-apt implicits %v", kaptStubs.Output.String(), kotlinc.Implicits.Strings())
}
- // Test that the kapt srcjar is extracted by the kotlinc and javac rules
- if kotlinc.Args["srcJars"] != kapt.Output.String() {
- t.Errorf("expected %q in kotlinc srcjars %v", kapt.Output.String(), kotlinc.Args["srcJars"])
+ // Test that the turbine-apt srcjar is a dependency of kotlinc and javac rules
+ if !inList(turbineApt.Output.String(), kotlinc.Implicits.Strings()) {
+ t.Errorf("expected %q in kotlinc implicits %v", turbineApt.Output.String(), kotlinc.Implicits.Strings())
}
- if javac.Args["srcJars"] != kapt.Output.String() {
- t.Errorf("expected %q in javac srcjars %v", kapt.Output.String(), kotlinc.Args["srcJars"])
+ if !inList(turbineApt.Output.String(), javac.Implicits.Strings()) {
+ t.Errorf("expected %q in javac implicits %v", turbineApt.Output.String(), javac.Implicits.Strings())
+ }
+
+ // Test that the turbine-apt srcjar is extracted by the kotlinc and javac rules
+ if kotlinc.Args["srcJars"] != turbineApt.Output.String() {
+ t.Errorf("expected %q in kotlinc srcjars %v", turbineApt.Output.String(), kotlinc.Args["srcJars"])
+ }
+ if javac.Args["srcJars"] != turbineApt.Output.String() {
+ t.Errorf("expected %q in javac srcjars %v", turbineApt.Output.String(), kotlinc.Args["srcJars"])
}
// Test that the processors are passed to kapt
expectedProcessorPath := "-P plugin:org.jetbrains.kotlin.kapt3:apclasspath=" + bar +
" -P plugin:org.jetbrains.kotlin.kapt3:apclasspath=" + baz
- if kapt.Args["kaptProcessorPath"] != expectedProcessorPath {
- t.Errorf("expected kaptProcessorPath %q, got %q", expectedProcessorPath, kapt.Args["kaptProcessorPath"])
+ if kaptStubs.Args["kaptProcessorPath"] != expectedProcessorPath {
+ t.Errorf("expected kaptProcessorPath %q, got %q", expectedProcessorPath, kaptStubs.Args["kaptProcessorPath"])
}
expectedProcessor := "-P plugin:org.jetbrains.kotlin.kapt3:processors=com.bar -P plugin:org.jetbrains.kotlin.kapt3:processors=com.baz"
- if kapt.Args["kaptProcessor"] != expectedProcessor {
- t.Errorf("expected kaptProcessor %q, got %q", expectedProcessor, kapt.Args["kaptProcessor"])
+ if kaptStubs.Args["kaptProcessor"] != expectedProcessor {
+ t.Errorf("expected kaptProcessor %q, got %q", expectedProcessor, kaptStubs.Args["kaptProcessor"])
+ }
+
+ // Test that the processors are passed to turbine-apt
+ expectedProcessorPath = "--processorpath " + bar + " " + baz
+ if !strings.Contains(turbineApt.Args["turbineFlags"], expectedProcessorPath) {
+ t.Errorf("expected turbine-apt processorpath %q, got %q", expectedProcessorPath, turbineApt.Args["turbineFlags"])
+ }
+ expectedProcessor = "--processors com.bar com.baz"
+ if !strings.Contains(turbineApt.Args["turbineFlags"], expectedProcessor) {
+ t.Errorf("expected turbine-apt processor %q, got %q", expectedProcessor, turbineApt.Args["turbineFlags"])
}
// Test that the processors are not passed to javac
diff --git a/rust/config/x86_linux_host.go b/rust/config/x86_linux_host.go
index 7608349..4d7c422 100644
--- a/rust/config/x86_linux_host.go
+++ b/rust/config/x86_linux_host.go
@@ -42,8 +42,6 @@
"-nodefaultlibs",
"-nostdlib",
"-Wl,--no-dynamic-linker",
- // for unwind
- "-lgcc", "-lgcc_eh",
}
linuxX86Rustflags = []string{}
linuxX86Linkflags = []string{}
diff --git a/rust/prebuilt.go b/rust/prebuilt.go
index 6f17272..6cdd07d 100644
--- a/rust/prebuilt.go
+++ b/rust/prebuilt.go
@@ -22,6 +22,7 @@
android.RegisterModuleType("rust_prebuilt_library", PrebuiltLibraryFactory)
android.RegisterModuleType("rust_prebuilt_dylib", PrebuiltDylibFactory)
android.RegisterModuleType("rust_prebuilt_rlib", PrebuiltRlibFactory)
+ android.RegisterModuleType("rust_prebuilt_proc_macro", PrebuiltProcMacroFactory)
}
type PrebuiltProperties struct {
@@ -38,8 +39,42 @@
Properties PrebuiltProperties
}
+type prebuiltProcMacroDecorator struct {
+ android.Prebuilt
+
+ *procMacroDecorator
+ Properties PrebuiltProperties
+}
+
+func PrebuiltProcMacroFactory() android.Module {
+ module, _ := NewPrebuiltProcMacro(android.HostSupportedNoCross)
+ return module.Init()
+}
+
+type rustPrebuilt interface {
+ prebuiltSrcs() []string
+ prebuilt() *android.Prebuilt
+}
+
+func NewPrebuiltProcMacro(hod android.HostOrDeviceSupported) (*Module, *prebuiltProcMacroDecorator) {
+ module, library := NewProcMacro(hod)
+ prebuilt := &prebuiltProcMacroDecorator{
+ procMacroDecorator: library,
+ }
+ module.compiler = prebuilt
+
+ addSrcSupplier(module, prebuilt)
+
+ return module, prebuilt
+}
+
var _ compiler = (*prebuiltLibraryDecorator)(nil)
var _ exportedFlagsProducer = (*prebuiltLibraryDecorator)(nil)
+var _ rustPrebuilt = (*prebuiltLibraryDecorator)(nil)
+
+var _ compiler = (*prebuiltProcMacroDecorator)(nil)
+var _ exportedFlagsProducer = (*prebuiltProcMacroDecorator)(nil)
+var _ rustPrebuilt = (*prebuiltProcMacroDecorator)(nil)
func PrebuiltLibraryFactory() android.Module {
module, _ := NewPrebuiltLibrary(android.HostAndDeviceSupported)
@@ -56,7 +91,7 @@
return module.Init()
}
-func addSrcSupplier(module android.PrebuiltInterface, prebuilt *prebuiltLibraryDecorator) {
+func addSrcSupplier(module android.PrebuiltInterface, prebuilt rustPrebuilt) {
srcsSupplier := func(_ android.BaseModuleContext, _ android.Module) []string {
return prebuilt.prebuiltSrcs()
}
@@ -152,3 +187,44 @@
func (prebuilt *prebuiltLibraryDecorator) prebuilt() *android.Prebuilt {
return &prebuilt.Prebuilt
}
+
+func (prebuilt *prebuiltProcMacroDecorator) prebuiltSrcs() []string {
+ srcs := prebuilt.Properties.Srcs
+ return srcs
+}
+
+func (prebuilt *prebuiltProcMacroDecorator) prebuilt() *android.Prebuilt {
+ return &prebuilt.Prebuilt
+}
+
+func (prebuilt *prebuiltProcMacroDecorator) compilerProps() []interface{} {
+ return append(prebuilt.procMacroDecorator.compilerProps(),
+ &prebuilt.Properties)
+}
+
+func (prebuilt *prebuiltProcMacroDecorator) compile(ctx ModuleContext, flags Flags, deps PathDeps) android.Path {
+ prebuilt.flagExporter.exportLinkDirs(android.PathsForModuleSrc(ctx, prebuilt.Properties.Link_dirs).Strings()...)
+ prebuilt.flagExporter.setProvider(ctx)
+
+ srcPath, paths := srcPathFromModuleSrcs(ctx, prebuilt.prebuiltSrcs())
+ if len(paths) > 0 {
+ ctx.PropertyErrorf("srcs", "prebuilt libraries can only have one entry in srcs (the prebuilt path)")
+ }
+ prebuilt.baseCompiler.unstrippedOutputFile = srcPath
+ return srcPath
+}
+
+func (prebuilt *prebuiltProcMacroDecorator) rustdoc(ctx ModuleContext, flags Flags,
+ deps PathDeps) android.OptionalPath {
+
+ return android.OptionalPath{}
+}
+
+func (prebuilt *prebuiltProcMacroDecorator) compilerDeps(ctx DepsContext, deps Deps) Deps {
+ deps = prebuilt.baseCompiler.compilerDeps(ctx, deps)
+ return deps
+}
+
+func (prebuilt *prebuiltProcMacroDecorator) nativeCoverage() bool {
+ return false
+}
diff --git a/rust/proc_macro.go b/rust/proc_macro.go
index 974c096..f8a4bbd 100644
--- a/rust/proc_macro.go
+++ b/rust/proc_macro.go
@@ -33,6 +33,7 @@
}
type procMacroInterface interface {
+ ProcMacro() bool
}
var _ compiler = (*procMacroDecorator)(nil)
@@ -90,6 +91,10 @@
return rlibAutoDep
}
+func (procMacro *procMacroDecorator) ProcMacro() bool {
+ return true
+}
+
func (procMacro *procMacroDecorator) everInstallable() bool {
// Proc_macros are never installed
return false
diff --git a/rust/rust.go b/rust/rust.go
index 1c718a4..d627261 100644
--- a/rust/rust.go
+++ b/rust/rust.go
@@ -27,6 +27,7 @@
cc_config "android/soong/cc/config"
"android/soong/fuzz"
"android/soong/rust/config"
+ "android/soong/snapshot"
)
var pctx = android.NewPackageContext("android/soong/rust")
@@ -806,6 +807,13 @@
return mod.Properties.Installable
}
+func (mod *Module) ProcMacro() bool {
+ if pm, ok := mod.compiler.(procMacroInterface); ok {
+ return pm.ProcMacro()
+ }
+ return false
+}
+
func (mod *Module) toolchain(ctx android.BaseModuleContext) config.Toolchain {
if mod.cachedToolchain == nil {
mod.cachedToolchain = config.FindToolchain(ctx.Os(), ctx.Arch())
@@ -920,12 +928,13 @@
}
apexInfo := actx.Provider(android.ApexInfoProvider).(android.ApexInfo)
- if !proptools.BoolDefault(mod.Installable(), mod.EverInstallable()) {
+ if !proptools.BoolDefault(mod.Installable(), mod.EverInstallable()) && !mod.ProcMacro() {
// If the module has been specifically configure to not be installed then
// hide from make as otherwise it will break when running inside make as the
// output path to install will not be specified. Not all uninstallable
// modules can be hidden from make as some are needed for resolving make
- // side dependencies.
+ // side dependencies. In particular, proc-macros need to be captured in the
+ // host snapshot.
mod.HideFromMake()
} else if !mod.installable(apexInfo) {
mod.SkipInstall()
@@ -1046,7 +1055,7 @@
}
func (mod *Module) Prebuilt() *android.Prebuilt {
- if p, ok := mod.compiler.(*prebuiltLibraryDecorator); ok {
+ if p, ok := mod.compiler.(rustPrebuilt); ok {
return p.prebuilt()
}
return nil
@@ -1501,6 +1510,7 @@
}
var _ android.HostToolProvider = (*Module)(nil)
+var _ snapshot.RelativeInstallPath = (*Module)(nil)
func (mod *Module) HostToolPath() android.OptionalPath {
if !mod.Host() {
@@ -1508,6 +1518,10 @@
}
if binary, ok := mod.compiler.(*binaryDecorator); ok {
return android.OptionalPathForPath(binary.baseCompiler.path)
+ } else if pm, ok := mod.compiler.(*procMacroDecorator); ok {
+ // Even though proc-macros aren't strictly "tools", since they target the compiler
+ // and act as compiler plugins, we treat them similarly.
+ return android.OptionalPathForPath(pm.baseCompiler.path)
}
return android.OptionalPath{}
}
diff --git a/scripts/hiddenapi/Android.bp b/scripts/hiddenapi/Android.bp
index 8a47c5d..07878f9 100644
--- a/scripts/hiddenapi/Android.bp
+++ b/scripts/hiddenapi/Android.bp
@@ -19,6 +19,52 @@
}
python_binary_host {
+ name: "analyze_bcpf",
+ main: "analyze_bcpf.py",
+ srcs: ["analyze_bcpf.py"],
+ // Make sure that the bpmodify tool is built.
+ data: [":bpmodify"],
+ libs: [
+ "signature_trie",
+ ],
+ version: {
+ py2: {
+ enabled: false,
+ },
+ py3: {
+ enabled: true,
+ embedded_launcher: true,
+ },
+ },
+}
+
+python_test_host {
+ name: "analyze_bcpf_test",
+ main: "analyze_bcpf_test.py",
+ srcs: [
+ "analyze_bcpf.py",
+ "analyze_bcpf_test.py",
+ ],
+ // Make sure that the bpmodify tool is built.
+ data: [":bpmodify"],
+ libs: [
+ "signature_trie",
+ ],
+ version: {
+ py2: {
+ enabled: false,
+ },
+ py3: {
+ enabled: true,
+ embedded_launcher: true,
+ },
+ },
+ test_options: {
+ unit_test: true,
+ },
+}
+
+python_binary_host {
name: "merge_csv",
main: "merge_csv.py",
srcs: ["merge_csv.py"],
diff --git a/scripts/hiddenapi/analyze_bcpf.py b/scripts/hiddenapi/analyze_bcpf.py
new file mode 100644
index 0000000..1ad8d07
--- /dev/null
+++ b/scripts/hiddenapi/analyze_bcpf.py
@@ -0,0 +1,1336 @@
+#!/usr/bin/env -S python -u
+#
+# Copyright (C) 2022 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.
+"""Analyze bootclasspath_fragment usage."""
+import argparse
+import dataclasses
+import enum
+import json
+import logging
+import os
+import re
+import shutil
+import subprocess
+import tempfile
+import textwrap
+import typing
+from enum import Enum
+
+import sys
+
+from signature_trie import signature_trie
+
+_STUB_FLAGS_FILE = "out/soong/hiddenapi/hiddenapi-stub-flags.txt"
+
+_FLAGS_FILE = "out/soong/hiddenapi/hiddenapi-flags.csv"
+
+_INCONSISTENT_FLAGS = "ERROR: Hidden API flags are inconsistent:"
+
+
+class BuildOperation:
+
+ def __init__(self, popen):
+ self.popen = popen
+ self.returncode = None
+
+ def lines(self):
+ """Return an iterator over the lines output by the build operation.
+
+ The lines have had any trailing white space, including the newline
+ stripped.
+ """
+ return newline_stripping_iter(self.popen.stdout.readline)
+
+ def wait(self, *args, **kwargs):
+ self.popen.wait(*args, **kwargs)
+ self.returncode = self.popen.returncode
+
+
+@dataclasses.dataclass()
+class FlagDiffs:
+ """Encapsulates differences in flags reported by the build"""
+
+ # Map from member signature to the (module flags, monolithic flags)
+ diffs: typing.Dict[str, typing.Tuple[str, str]]
+
+
+@dataclasses.dataclass()
+class ModuleInfo:
+ """Provides access to the generated module-info.json file.
+
+ This is used to find the location of the file within which specific modules
+ are defined.
+ """
+
+ modules: typing.Dict[str, typing.Dict[str, typing.Any]]
+
+ @staticmethod
+ def load(filename):
+ with open(filename, "r", encoding="utf8") as f:
+ j = json.load(f)
+ return ModuleInfo(j)
+
+ def _module(self, module_name):
+ """Find module by name in module-info.json file"""
+ if module_name in self.modules:
+ return self.modules[module_name]
+
+ raise Exception(f"Module {module_name} could not be found")
+
+ def module_path(self, module_name):
+ module = self._module(module_name)
+ # The "path" is actually a list of paths, one for each class of module
+ # but as the modules are all created from bp files if a module does
+ # create multiple classes of make modules they should all have the same
+ # path.
+ paths = module["path"]
+ unique_paths = set(paths)
+ if len(unique_paths) != 1:
+ raise Exception(f"Expected module '{module_name}' to have a "
+ f"single unique path but found {unique_paths}")
+ return paths[0]
+
+
+def extract_indent(line):
+ return re.match(r"([ \t]*)", line).group(1)
+
+
+_SPECIAL_PLACEHOLDER: str = "SPECIAL_PLACEHOLDER"
+
+
+@dataclasses.dataclass
+class BpModifyRunner:
+
+ bpmodify_path: str
+
+ def add_values_to_property(self, property_name, values, module_name,
+ bp_file):
+ cmd = [
+ self.bpmodify_path, "-a", values, "-property", property_name, "-m",
+ module_name, "-w", bp_file, bp_file
+ ]
+
+ logging.debug(" ".join(cmd))
+ subprocess.run(
+ cmd,
+ stderr=subprocess.STDOUT,
+ stdout=log_stream_for_subprocess(),
+ check=True)
+
+
+@dataclasses.dataclass
+class FileChange:
+ path: str
+
+ description: str
+
+ def __lt__(self, other):
+ return self.path < other.path
+
+
+class PropertyChangeAction(Enum):
+ """Allowable actions that are supported by HiddenApiPropertyChange."""
+
+ # New values are appended to any existing values.
+ APPEND = 1
+
+ # New values replace any existing values.
+ REPLACE = 2
+
+
+@dataclasses.dataclass
+class HiddenApiPropertyChange:
+
+ property_name: str
+
+ values: typing.List[str]
+
+ property_comment: str = ""
+
+ # The action that indicates how this change is applied.
+ action: PropertyChangeAction = PropertyChangeAction.APPEND
+
+ def snippet(self, indent):
+ snippet = "\n"
+ snippet += format_comment_as_text(self.property_comment, indent)
+ snippet += f"{indent}{self.property_name}: ["
+ if self.values:
+ snippet += "\n"
+ for value in self.values:
+ snippet += f'{indent} "{value}",\n'
+ snippet += f"{indent}"
+ snippet += "],\n"
+ return snippet
+
+ def fix_bp_file(self, bcpf_bp_file, bcpf, bpmodify_runner: BpModifyRunner):
+ # Add an additional placeholder value to identify the modification that
+ # bpmodify makes.
+ bpmodify_values = [_SPECIAL_PLACEHOLDER]
+
+ if self.action == PropertyChangeAction.APPEND:
+ # If adding the values to the existing values then pass the new
+ # values to bpmodify.
+ bpmodify_values.extend(self.values)
+ elif self.action == PropertyChangeAction.REPLACE:
+ # If replacing the existing values then it is not possible to use
+ # bpmodify for that directly. It could be used twice to remove the
+ # existing property and then add a new one but that does not remove
+ # any related comments and loses the position of the existing
+ # property as the new property is always added to the end of the
+ # containing block.
+ #
+ # So, instead of passing the new values to bpmodify this this just
+ # adds an extra placeholder to force bpmodify to format the list
+ # across multiple lines to ensure a consistent structure for the
+ # code that removes all the existing values and adds the new ones.
+ #
+ # This placeholder has to be different to the other placeholder as
+ # bpmodify dedups values.
+ bpmodify_values.append(_SPECIAL_PLACEHOLDER + "_REPLACE")
+ else:
+ raise ValueError(f"unknown action {self.action}")
+
+ packages = ",".join(bpmodify_values)
+ bpmodify_runner.add_values_to_property(
+ f"hidden_api.{self.property_name}", packages, bcpf, bcpf_bp_file)
+
+ with open(bcpf_bp_file, "r", encoding="utf8") as tio:
+ lines = tio.readlines()
+ lines = [line.rstrip("\n") for line in lines]
+
+ if self.fixup_bpmodify_changes(bcpf_bp_file, lines):
+ with open(bcpf_bp_file, "w", encoding="utf8") as tio:
+ for line in lines:
+ print(line, file=tio)
+
+ def fixup_bpmodify_changes(self, bcpf_bp_file, lines):
+ """Fixup the output of bpmodify.
+
+ The bpmodify tool does not support all the capabilities that this needs
+ so it is used to do what it can, including marking the place in the
+ Android.bp file where it makes its changes and then this gets passed a
+ list of lines from that file which it then modifies to complete the
+ change.
+
+ This analyzes the list of lines to find the indices of the significant
+ lines and then applies some changes. As those changes can insert and
+ delete lines (changing the indices of following lines) the changes are
+ generally done in reverse order starting from the end and working
+ towards the beginning. That ensures that the changes do not invalidate
+ the indices of following lines.
+ """
+
+ # Find the line containing the placeholder that has been inserted.
+ place_holder_index = -1
+ for i, line in enumerate(lines):
+ if _SPECIAL_PLACEHOLDER in line:
+ place_holder_index = i
+ break
+ if place_holder_index == -1:
+ logging.debug("Could not find %s in %s", _SPECIAL_PLACEHOLDER,
+ bcpf_bp_file)
+ return False
+
+ # Remove the place holder. Do this before inserting the comment as that
+ # would change the location of the place holder in the list.
+ place_holder_line = lines[place_holder_index]
+ if place_holder_line.endswith("],"):
+ place_holder_line = place_holder_line.replace(
+ f'"{_SPECIAL_PLACEHOLDER}"', "")
+ lines[place_holder_index] = place_holder_line
+ else:
+ del lines[place_holder_index]
+
+ # Scan forward to the end of the property block to remove a blank line
+ # that bpmodify inserts.
+ end_property_array_index = -1
+ for i in range(place_holder_index, len(lines)):
+ line = lines[i]
+ if line.endswith("],"):
+ end_property_array_index = i
+ break
+ if end_property_array_index == -1:
+ logging.debug("Could not find end of property array in %s",
+ bcpf_bp_file)
+ return False
+
+ # If bdmodify inserted a blank line afterwards then remove it.
+ if (not lines[end_property_array_index + 1] and
+ lines[end_property_array_index + 2].endswith("},")):
+ del lines[end_property_array_index + 1]
+
+ # Scan back to find the preceding property line.
+ property_line_index = -1
+ for i in range(place_holder_index, 0, -1):
+ line = lines[i]
+ if line.lstrip().startswith(f"{self.property_name}: ["):
+ property_line_index = i
+ break
+ if property_line_index == -1:
+ logging.debug("Could not find property line in %s", bcpf_bp_file)
+ return False
+
+ # If this change is replacing the existing values then they need to be
+ # removed and replaced with the new values. That will change the lines
+ # after the property but it is necessary to do here as the following
+ # code operates on earlier lines.
+ if self.action == PropertyChangeAction.REPLACE:
+ # This removes the existing values and replaces them with the new
+ # values.
+ indent = extract_indent(lines[property_line_index + 1])
+ insert = [f'{indent}"{x}",' for x in self.values]
+ lines[property_line_index + 1:end_property_array_index] = insert
+ if not self.values:
+ # If the property has no values then merge the ], onto the
+ # same line as the property name.
+ del lines[property_line_index + 1]
+ lines[property_line_index] = lines[property_line_index] + "],"
+
+ # Only insert a comment if the property does not already have a comment.
+ line_preceding_property = lines[(property_line_index - 1)]
+ if (self.property_comment and
+ not re.match("([ \t]+)// ", line_preceding_property)):
+ # Extract the indent from the property line and use it to format the
+ # comment.
+ indent = extract_indent(lines[property_line_index])
+ comment_lines = format_comment_as_lines(self.property_comment,
+ indent)
+
+ # If the line before the comment is not blank then insert an extra
+ # blank line at the beginning of the comment.
+ if line_preceding_property:
+ comment_lines.insert(0, "")
+
+ # Insert the comment before the property.
+ lines[property_line_index:property_line_index] = comment_lines
+ return True
+
+
+@dataclasses.dataclass()
+class Result:
+ """Encapsulates the result of the analysis."""
+
+ # The diffs in the flags.
+ diffs: typing.Optional[FlagDiffs] = None
+
+ # The bootclasspath_fragment hidden API properties changes.
+ property_changes: typing.List[HiddenApiPropertyChange] = dataclasses.field(
+ default_factory=list)
+
+ # The list of file changes.
+ file_changes: typing.List[FileChange] = dataclasses.field(
+ default_factory=list)
+
+
+class ClassProvider(enum.Enum):
+ """The source of a class found during the hidden API processing"""
+ BCPF = "bcpf"
+ OTHER = "other"
+
+
+# A fake member to use when using the signature trie to compute the package
+# properties from hidden API flags. This is needed because while that
+# computation only cares about classes the trie expects a class to be an
+# interior node but without a member it makes the class a leaf node. That causes
+# problems when analyzing inner classes as the outer class is a leaf node for
+# its own entry but is used as an interior node for inner classes.
+_FAKE_MEMBER = ";->fake()V"
+
+
+@dataclasses.dataclass()
+class BcpfAnalyzer:
+ # Path to this tool.
+ tool_path: str
+
+ # Directory pointed to by ANDROID_BUILD_OUT
+ top_dir: str
+
+ # Directory pointed to by OUT_DIR of {top_dir}/out if that is not set.
+ out_dir: str
+
+ # Directory pointed to by ANDROID_PRODUCT_OUT.
+ product_out_dir: str
+
+ # The name of the bootclasspath_fragment module.
+ bcpf: str
+
+ # The name of the apex module containing {bcpf}, only used for
+ # informational purposes.
+ apex: str
+
+ # The name of the sdk module containing {bcpf}, only used for
+ # informational purposes.
+ sdk: str
+
+ # If true then this will attempt to automatically fix any issues that are
+ # found.
+ fix: bool = False
+
+ # All the signatures, loaded from all-flags.csv, initialized by
+ # load_all_flags().
+ _signatures: typing.Set[str] = dataclasses.field(default_factory=set)
+
+ # All the classes, loaded from all-flags.csv, initialized by
+ # load_all_flags().
+ _classes: typing.Set[str] = dataclasses.field(default_factory=set)
+
+ # Information loaded from module-info.json, initialized by
+ # load_module_info().
+ module_info: ModuleInfo = None
+
+ @staticmethod
+ def reformat_report_test(text):
+ return re.sub(r"(.)\n([^\s])", r"\1 \2", text)
+
+ def report(self, text, **kwargs):
+ # Concatenate lines that are not separated by a blank line together to
+ # eliminate formatting applied to the supplied text to adhere to python
+ # line length limitations.
+ text = self.reformat_report_test(text)
+ logging.info("%s", text, **kwargs)
+
+ def run_command(self, cmd, *args, **kwargs):
+ cmd_line = " ".join(cmd)
+ logging.debug("Running %s", cmd_line)
+ subprocess.run(
+ cmd,
+ *args,
+ check=True,
+ cwd=self.top_dir,
+ stderr=subprocess.STDOUT,
+ stdout=log_stream_for_subprocess(),
+ text=True,
+ **kwargs)
+
+ @property
+ def signatures(self):
+ if not self._signatures:
+ raise Exception("signatures has not been initialized")
+ return self._signatures
+
+ @property
+ def classes(self):
+ if not self._classes:
+ raise Exception("classes has not been initialized")
+ return self._classes
+
+ def load_all_flags(self):
+ all_flags = self.find_bootclasspath_fragment_output_file(
+ "all-flags.csv")
+
+ # Extract the set of signatures and a separate set of classes produced
+ # by the bootclasspath_fragment.
+ with open(all_flags, "r", encoding="utf8") as f:
+ for line in newline_stripping_iter(f.readline):
+ signature = self.line_to_signature(line)
+ self._signatures.add(signature)
+ class_name = self.signature_to_class(signature)
+ self._classes.add(class_name)
+
+ def load_module_info(self):
+ module_info_file = os.path.join(self.product_out_dir,
+ "module-info.json")
+ self.report(f"""
+Making sure that {module_info_file} is up to date.
+""")
+ output = self.build_file_read_output(module_info_file)
+ lines = output.lines()
+ for line in lines:
+ logging.debug("%s", line)
+ output.wait(timeout=10)
+ if output.returncode:
+ raise Exception(f"Error building {module_info_file}")
+ abs_module_info_file = os.path.join(self.top_dir, module_info_file)
+ self.module_info = ModuleInfo.load(abs_module_info_file)
+
+ @staticmethod
+ def line_to_signature(line):
+ return line.split(",")[0]
+
+ @staticmethod
+ def signature_to_class(signature):
+ return signature.split(";->")[0]
+
+ @staticmethod
+ def to_parent_package(pkg_or_class):
+ return pkg_or_class.rsplit("/", 1)[0]
+
+ def module_path(self, module_name):
+ return self.module_info.module_path(module_name)
+
+ def module_out_dir(self, module_name):
+ module_path = self.module_path(module_name)
+ return os.path.join(self.out_dir, "soong/.intermediates", module_path,
+ module_name)
+
+ def find_bootclasspath_fragment_output_file(self, basename, required=True):
+ # Find the output file of the bootclasspath_fragment with the specified
+ # base name.
+ found_file = ""
+ bcpf_out_dir = self.module_out_dir(self.bcpf)
+ for (dirpath, _, filenames) in os.walk(bcpf_out_dir):
+ for f in filenames:
+ if f == basename:
+ found_file = os.path.join(dirpath, f)
+ break
+ if not found_file and required:
+ raise Exception(f"Could not find {basename} in {bcpf_out_dir}")
+ return found_file
+
+ def analyze(self):
+ """Analyze a bootclasspath_fragment module.
+
+ Provides help in resolving any existing issues and provides
+ optimizations that can be applied.
+ """
+ self.report(f"Analyzing bootclasspath_fragment module {self.bcpf}")
+ self.report(f"""
+Run this tool to help initialize a bootclasspath_fragment module. Before you
+start make sure that:
+
+1. The current checkout is up to date.
+
+2. The environment has been initialized using lunch, e.g.
+ lunch aosp_arm64-userdebug
+
+3. You have added a bootclasspath_fragment module to the appropriate Android.bp
+file. Something like this:
+
+ bootclasspath_fragment {{
+ name: "{self.bcpf}",
+ contents: [
+ "...",
+ ],
+
+ // The bootclasspath_fragments that provide APIs on which this depends.
+ fragments: [
+ {{
+ apex: "com.android.art",
+ module: "art-bootclasspath-fragment",
+ }},
+ ],
+ }}
+
+4. You have added it to the platform_bootclasspath module in
+frameworks/base/boot/Android.bp. Something like this:
+
+ platform_bootclasspath {{
+ name: "platform-bootclasspath",
+ fragments: [
+ ...
+ {{
+ apex: "{self.apex}",
+ module: "{self.bcpf}",
+ }},
+ ],
+ }}
+
+5. You have added an sdk module. Something like this:
+
+ sdk {{
+ name: "{self.sdk}",
+ bootclasspath_fragments: ["{self.bcpf}"],
+ }}
+""")
+
+ # Make sure that the module-info.json file is up to date.
+ self.load_module_info()
+
+ self.report("""
+Cleaning potentially stale files.
+""")
+ # Remove the out/soong/hiddenapi files.
+ shutil.rmtree(f"{self.out_dir}/soong/hiddenapi", ignore_errors=True)
+
+ # Remove any bootclasspath_fragment output files.
+ shutil.rmtree(self.module_out_dir(self.bcpf), ignore_errors=True)
+
+ self.build_monolithic_stubs_flags()
+
+ result = Result()
+
+ self.build_monolithic_flags(result)
+ self.analyze_hiddenapi_package_properties(result)
+ self.explain_how_to_check_signature_patterns()
+
+ # If there were any changes that need to be made to the Android.bp
+ # file then either apply or report them.
+ if result.property_changes:
+ bcpf_dir = self.module_info.module_path(self.bcpf)
+ bcpf_bp_file = os.path.join(self.top_dir, bcpf_dir, "Android.bp")
+ if self.fix:
+ tool_dir = os.path.dirname(self.tool_path)
+ bpmodify_path = os.path.join(tool_dir, "bpmodify")
+ bpmodify_runner = BpModifyRunner(bpmodify_path)
+ for property_change in result.property_changes:
+ property_change.fix_bp_file(bcpf_bp_file, self.bcpf,
+ bpmodify_runner)
+
+ result.file_changes.append(
+ self.new_file_change(
+ bcpf_bp_file,
+ f"Updated hidden_api properties of '{self.bcpf}'"))
+
+ else:
+ hiddenapi_snippet = ""
+ for property_change in result.property_changes:
+ hiddenapi_snippet += property_change.snippet(" ")
+
+ # Remove leading and trailing blank lines.
+ hiddenapi_snippet = hiddenapi_snippet.strip("\n")
+
+ result.file_changes.append(
+ self.new_file_change(
+ bcpf_bp_file, f"""
+Add the following snippet into the {self.bcpf} bootclasspath_fragment module
+in the {bcpf_dir}/Android.bp file. If the hidden_api block already exists then
+merge these properties into it.
+
+ hidden_api: {{
+{hiddenapi_snippet}
+ }},
+"""))
+
+ if result.file_changes:
+ if self.fix:
+ file_change_message = """
+The following files were modified by this script:"""
+ else:
+ file_change_message = """
+The following modifications need to be made:"""
+
+ self.report(f"""
+{file_change_message}""")
+ result.file_changes.sort()
+ for file_change in result.file_changes:
+ self.report(f"""
+ {file_change.path}
+ {file_change.description}
+""".lstrip("\n"))
+
+ if not self.fix:
+ self.report("""
+Run the command again with the --fix option to automatically make the above
+changes.
+""".lstrip())
+
+ def new_file_change(self, file, description):
+ return FileChange(
+ path=os.path.relpath(file, self.top_dir), description=description)
+
+ def check_inconsistent_flag_lines(self, significant, module_line,
+ monolithic_line, separator_line):
+ if not (module_line.startswith("< ") and
+ monolithic_line.startswith("> ") and not separator_line):
+ # Something went wrong.
+ self.report(f"""Invalid build output detected:
+ module_line: "{module_line}"
+ monolithic_line: "{monolithic_line}"
+ separator_line: "{separator_line}"
+""")
+ sys.exit(1)
+
+ if significant:
+ logging.debug("%s", module_line)
+ logging.debug("%s", monolithic_line)
+ logging.debug("%s", separator_line)
+
+ def scan_inconsistent_flags_report(self, lines):
+ """Scans a hidden API flags report
+
+ The hidden API inconsistent flags report which looks something like
+ this.
+
+ < out/soong/.intermediates/.../filtered-stub-flags.csv
+ > out/soong/hiddenapi/hiddenapi-stub-flags.txt
+
+ < Landroid/compat/Compatibility;->clearOverrides()V
+ > Landroid/compat/Compatibility;->clearOverrides()V,core-platform-api
+
+ """
+
+ # The basic format of an entry in the inconsistent flags report is:
+ # <module specific flag>
+ # <monolithic flag>
+ # <separator>
+ #
+ # Wrap the lines iterator in an iterator which returns a tuple
+ # consisting of the three separate lines.
+ triples = zip(lines, lines, lines)
+
+ module_line, monolithic_line, separator_line = next(triples)
+ significant = False
+ bcpf_dir = self.module_info.module_path(self.bcpf)
+ if os.path.join(bcpf_dir, self.bcpf) in module_line:
+ # These errors are related to the bcpf being analyzed so
+ # keep them.
+ significant = True
+ else:
+ self.report(f"Filtering out errors related to {module_line}")
+
+ self.check_inconsistent_flag_lines(significant, module_line,
+ monolithic_line, separator_line)
+
+ diffs = {}
+ for module_line, monolithic_line, separator_line in triples:
+ self.check_inconsistent_flag_lines(significant, module_line,
+ monolithic_line, "")
+
+ module_parts = module_line.removeprefix("< ").split(",")
+ module_signature = module_parts[0]
+ module_flags = module_parts[1:]
+
+ monolithic_parts = monolithic_line.removeprefix("> ").split(",")
+ monolithic_signature = monolithic_parts[0]
+ monolithic_flags = monolithic_parts[1:]
+
+ if module_signature != monolithic_signature:
+ # Something went wrong.
+ self.report(f"""Inconsistent signatures detected:
+ module_signature: "{module_signature}"
+ monolithic_signature: "{monolithic_signature}"
+""")
+ sys.exit(1)
+
+ diffs[module_signature] = (module_flags, monolithic_flags)
+
+ if separator_line:
+ # If the separator line is not blank then it is the end of the
+ # current report, and possibly the start of another.
+ return separator_line, diffs
+
+ return "", diffs
+
+ def build_file_read_output(self, filename):
+ # Make sure the filename is relative to top if possible as the build
+ # may be using relative paths as the target.
+ rel_filename = filename.removeprefix(self.top_dir)
+ cmd = ["build/soong/soong_ui.bash", "--make-mode", rel_filename]
+ cmd_line = " ".join(cmd)
+ logging.debug("%s", cmd_line)
+ # pylint: disable=consider-using-with
+ output = subprocess.Popen(
+ cmd,
+ cwd=self.top_dir,
+ stderr=subprocess.STDOUT,
+ stdout=subprocess.PIPE,
+ text=True,
+ )
+ return BuildOperation(popen=output)
+
+ def build_hiddenapi_flags(self, filename):
+ output = self.build_file_read_output(filename)
+
+ lines = output.lines()
+ diffs = None
+ for line in lines:
+ logging.debug("%s", line)
+ while line == _INCONSISTENT_FLAGS:
+ line, diffs = self.scan_inconsistent_flags_report(lines)
+
+ output.wait(timeout=10)
+ if output.returncode != 0:
+ logging.debug("Command failed with %s", output.returncode)
+ else:
+ logging.debug("Command succeeded")
+
+ return diffs
+
+ def build_monolithic_stubs_flags(self):
+ self.report(f"""
+Attempting to build {_STUB_FLAGS_FILE} to verify that the
+bootclasspath_fragment has the correct API stubs available...
+""")
+
+ # Build the hiddenapi-stubs-flags.txt file.
+ diffs = self.build_hiddenapi_flags(_STUB_FLAGS_FILE)
+ if diffs:
+ self.report(f"""
+There is a discrepancy between the stub API derived flags created by the
+bootclasspath_fragment and the platform_bootclasspath. See preceding error
+messages to see which flags are inconsistent. The inconsistencies can occur for
+a couple of reasons:
+
+If you are building against prebuilts of the Android SDK, e.g. by using
+TARGET_BUILD_APPS then the prebuilt versions of the APIs this
+bootclasspath_fragment depends upon are out of date and need updating. See
+go/update-prebuilts for help.
+
+Otherwise, this is happening because there are some stub APIs that are either
+provided by or used by the contents of the bootclasspath_fragment but which are
+not available to it. There are 4 ways to handle this:
+
+1. A java_sdk_library in the contents property will automatically make its stub
+ APIs available to the bootclasspath_fragment so nothing needs to be done.
+
+2. If the API provided by the bootclasspath_fragment is created by an api_only
+ java_sdk_library (or a java_library that compiles files generated by a
+ separate droidstubs module then it cannot be added to the contents and
+ instead must be added to the api.stubs property, e.g.
+
+ bootclasspath_fragment {{
+ name: "{self.bcpf}",
+ ...
+ api: {{
+ stubs: ["$MODULE-api-only"],"
+ }},
+ }}
+
+3. If the contents use APIs provided by another bootclasspath_fragment then
+ it needs to be added to the fragments property, e.g.
+
+ bootclasspath_fragment {{
+ name: "{self.bcpf}",
+ ...
+ // The bootclasspath_fragments that provide APIs on which this depends.
+ fragments: [
+ ...
+ {{
+ apex: "com.android.other",
+ module: "com.android.other-bootclasspath-fragment",
+ }},
+ ],
+ }}
+
+4. If the contents use APIs from a module that is not part of another
+ bootclasspath_fragment then it must be added to the additional_stubs
+ property, e.g.
+
+ bootclasspath_fragment {{
+ name: "{self.bcpf}",
+ ...
+ additional_stubs: ["android-non-updatable"],
+ }}
+
+ Like the api.stubs property these are typically java_sdk_library modules but
+ can be java_library too.
+
+ Note: The "android-non-updatable" is treated as if it was a java_sdk_library
+ which it is not at the moment but will be in future.
+""")
+
+ return diffs
+
+ def build_monolithic_flags(self, result):
+ self.report(f"""
+Attempting to build {_FLAGS_FILE} to verify that the
+bootclasspath_fragment has the correct hidden API flags...
+""")
+
+ # Build the hiddenapi-flags.csv file and extract any differences in
+ # the flags between this bootclasspath_fragment and the monolithic
+ # files.
+ result.diffs = self.build_hiddenapi_flags(_FLAGS_FILE)
+
+ # Load information from the bootclasspath_fragment's all-flags.csv file.
+ self.load_all_flags()
+
+ if result.diffs:
+ self.report(f"""
+There is a discrepancy between the hidden API flags created by the
+bootclasspath_fragment and the platform_bootclasspath. See preceding error
+messages to see which flags are inconsistent. The inconsistencies can occur for
+a couple of reasons:
+
+If you are building against prebuilts of this bootclasspath_fragment then the
+prebuilt version of the sdk snapshot (specifically the hidden API flag files)
+are inconsistent with the prebuilt version of the apex {self.apex}. Please
+ensure that they are both updated from the same build.
+
+1. There are custom hidden API flags specified in the one of the files in
+ frameworks/base/boot/hiddenapi which apply to the bootclasspath_fragment but
+ which are not supplied to the bootclasspath_fragment module.
+
+2. The bootclasspath_fragment specifies invalid "package_prefixes" or
+ "split_packages" properties that match packages and classes that it does not
+ provide.
+
+""")
+
+ # Check to see if there are any hiddenapi related properties that
+ # need to be added to the
+ self.report("""
+Checking custom hidden API flags....
+""")
+ self.check_frameworks_base_boot_hidden_api_files(result)
+
+ def report_hidden_api_flag_file_changes(self, result, property_name,
+ flags_file, rel_bcpf_flags_file,
+ bcpf_flags_file):
+ matched_signatures = set()
+ # Open the flags file to read the flags from.
+ with open(flags_file, "r", encoding="utf8") as f:
+ for signature in newline_stripping_iter(f.readline):
+ if signature in self.signatures:
+ # The signature is provided by the bootclasspath_fragment so
+ # it will need to be moved to the bootclasspath_fragment
+ # specific file.
+ matched_signatures.add(signature)
+
+ # If the bootclasspath_fragment specific flags file is not empty
+ # then it contains flags. That could either be new flags just moved
+ # from frameworks/base or previous contents of the file. In either
+ # case the file must not be removed.
+ if matched_signatures:
+ insert = textwrap.indent("\n".join(matched_signatures),
+ " ")
+ result.file_changes.append(
+ self.new_file_change(
+ flags_file, f"""Remove the following entries:
+{insert}
+"""))
+
+ result.file_changes.append(
+ self.new_file_change(
+ bcpf_flags_file, f"""Add the following entries:
+{insert}
+"""))
+
+ result.property_changes.append(
+ HiddenApiPropertyChange(
+ property_name=property_name,
+ values=[rel_bcpf_flags_file],
+ ))
+
+ def fix_hidden_api_flag_files(self, result, property_name, flags_file,
+ rel_bcpf_flags_file, bcpf_flags_file):
+ # Read the file in frameworks/base/boot/hiddenapi/<file> copy any
+ # flags that relate to the bootclasspath_fragment into a local
+ # file in the hiddenapi subdirectory.
+ tmp_flags_file = flags_file + ".tmp"
+
+ # Make sure the directory containing the bootclasspath_fragment specific
+ # hidden api flags exists.
+ os.makedirs(os.path.dirname(bcpf_flags_file), exist_ok=True)
+
+ bcpf_flags_file_exists = os.path.exists(bcpf_flags_file)
+
+ matched_signatures = set()
+ # Open the flags file to read the flags from.
+ with open(flags_file, "r", encoding="utf8") as f:
+ # Open a temporary file to write the flags (minus any removed
+ # flags).
+ with open(tmp_flags_file, "w", encoding="utf8") as t:
+ # Open the bootclasspath_fragment file for append just in
+ # case it already exists.
+ with open(bcpf_flags_file, "a", encoding="utf8") as b:
+ for line in iter(f.readline, ""):
+ signature = line.rstrip()
+ if signature in self.signatures:
+ # The signature is provided by the
+ # bootclasspath_fragment so write it to the new
+ # bootclasspath_fragment specific file.
+ print(line, file=b, end="")
+ matched_signatures.add(signature)
+ else:
+ # The signature is NOT provided by the
+ # bootclasspath_fragment. Copy it to the new
+ # monolithic file.
+ print(line, file=t, end="")
+
+ # If the bootclasspath_fragment specific flags file is not empty
+ # then it contains flags. That could either be new flags just moved
+ # from frameworks/base or previous contents of the file. In either
+ # case the file must not be removed.
+ if matched_signatures:
+ # There are custom flags related to the bootclasspath_fragment
+ # so replace the frameworks/base/boot/hiddenapi file with the
+ # file that does not contain those flags.
+ shutil.move(tmp_flags_file, flags_file)
+
+ result.file_changes.append(
+ self.new_file_change(flags_file,
+ f"Removed '{self.bcpf}' specific entries"))
+
+ result.property_changes.append(
+ HiddenApiPropertyChange(
+ property_name=property_name,
+ values=[rel_bcpf_flags_file],
+ ))
+
+ # Make sure that the files are sorted.
+ self.run_command([
+ "tools/platform-compat/hiddenapi/sort_api.sh",
+ bcpf_flags_file,
+ ])
+
+ if bcpf_flags_file_exists:
+ desc = f"Added '{self.bcpf}' specific entries"
+ else:
+ desc = f"Created with '{self.bcpf}' specific entries"
+ result.file_changes.append(
+ self.new_file_change(bcpf_flags_file, desc))
+ else:
+ # There are no custom flags related to the
+ # bootclasspath_fragment so clean up the working files.
+ os.remove(tmp_flags_file)
+ if not bcpf_flags_file_exists:
+ os.remove(bcpf_flags_file)
+
+ def check_frameworks_base_boot_hidden_api_files(self, result):
+ hiddenapi_dir = os.path.join(self.top_dir,
+ "frameworks/base/boot/hiddenapi")
+ for basename in sorted(os.listdir(hiddenapi_dir)):
+ if not (basename.startswith("hiddenapi-") and
+ basename.endswith(".txt")):
+ continue
+
+ flags_file = os.path.join(hiddenapi_dir, basename)
+
+ logging.debug("Checking %s for flags related to %s", flags_file,
+ self.bcpf)
+
+ # Map the file name in frameworks/base/boot/hiddenapi into a
+ # slightly more meaningful name for use by the
+ # bootclasspath_fragment.
+ if basename == "hiddenapi-max-target-o.txt":
+ basename = "hiddenapi-max-target-o-low-priority.txt"
+ elif basename == "hiddenapi-max-target-r-loprio.txt":
+ basename = "hiddenapi-max-target-r-low-priority.txt"
+
+ property_name = basename.removeprefix("hiddenapi-")
+ property_name = property_name.removesuffix(".txt")
+ property_name = property_name.replace("-", "_")
+
+ rel_bcpf_flags_file = f"hiddenapi/{basename}"
+ bcpf_dir = self.module_info.module_path(self.bcpf)
+ bcpf_flags_file = os.path.join(self.top_dir, bcpf_dir,
+ rel_bcpf_flags_file)
+
+ if self.fix:
+ self.fix_hidden_api_flag_files(result, property_name,
+ flags_file, rel_bcpf_flags_file,
+ bcpf_flags_file)
+ else:
+ self.report_hidden_api_flag_file_changes(
+ result, property_name, flags_file, rel_bcpf_flags_file,
+ bcpf_flags_file)
+
+ @staticmethod
+ def split_package_comment(split_packages):
+ if split_packages:
+ return textwrap.dedent("""
+ The following packages contain classes from other modules on the
+ bootclasspath. That means that the hidden API flags for this
+ module has to explicitly list every single class this module
+ provides in that package to differentiate them from the classes
+ provided by other modules. That can include private classes that
+ are not part of the API.
+ """).strip("\n")
+
+ return "This module does not contain any split packages."
+
+ @staticmethod
+ def package_prefixes_comment():
+ return textwrap.dedent("""
+ The following packages and all their subpackages currently only
+ contain classes from this bootclasspath_fragment. Listing a package
+ here won't prevent other bootclasspath modules from adding classes
+ in any of those packages but it will prevent them from adding those
+ classes into an API surface, e.g. public, system, etc.. Doing so
+ will result in a build failure due to inconsistent flags.
+ """).strip("\n")
+
+ def analyze_hiddenapi_package_properties(self, result):
+ split_packages, single_packages, package_prefixes = \
+ self.compute_hiddenapi_package_properties()
+
+ # TODO(b/202154151): Find those classes in split packages that are not
+ # part of an API, i.e. are an internal implementation class, and so
+ # can, and should, be safely moved out of the split packages.
+
+ result.property_changes.append(
+ HiddenApiPropertyChange(
+ property_name="split_packages",
+ values=split_packages,
+ property_comment=self.split_package_comment(split_packages),
+ action=PropertyChangeAction.REPLACE,
+ ))
+
+ if split_packages:
+ self.report(f"""
+bootclasspath_fragment {self.bcpf} contains classes in packages that also
+contain classes provided by other sources, those packages are called split
+packages. Split packages should be avoided where possible but are often
+unavoidable when modularizing existing code.
+
+The hidden api processing needs to know which packages are split (and conversely
+which are not) so that it can optimize the hidden API flags to remove
+unnecessary implementation details.
+""")
+
+ self.report("""
+By default (for backwards compatibility) the bootclasspath_fragment assumes that
+all packages are split unless one of the package_prefixes or split_packages
+properties are specified. While that is safe it is not optimal and can lead to
+unnecessary implementation details leaking into the hidden API flags. Adding an
+empty split_packages property allows the flags to be optimized and remove any
+unnecessary implementation details.
+""")
+
+ if single_packages:
+ result.property_changes.append(
+ HiddenApiPropertyChange(
+ property_name="single_packages",
+ values=single_packages,
+ property_comment=textwrap.dedent("""
+ The following packages currently only contain classes from
+ this bootclasspath_fragment but some of their sub-packages
+ contain classes from other bootclasspath modules. Packages
+ should only be listed here when necessary for legacy
+ purposes, new packages should match a package prefix.
+ """),
+ action=PropertyChangeAction.REPLACE,
+ ))
+
+ if package_prefixes:
+ result.property_changes.append(
+ HiddenApiPropertyChange(
+ property_name="package_prefixes",
+ values=package_prefixes,
+ property_comment=self.package_prefixes_comment(),
+ action=PropertyChangeAction.REPLACE,
+ ))
+
+ def explain_how_to_check_signature_patterns(self):
+ signature_patterns_files = self.find_bootclasspath_fragment_output_file(
+ "signature-patterns.csv", required=False)
+ if signature_patterns_files:
+ signature_patterns_files = signature_patterns_files.removeprefix(
+ self.top_dir)
+
+ self.report(f"""
+The purpose of the hiddenapi split_packages and package_prefixes properties is
+to allow the removal of implementation details from the hidden API flags to
+reduce the coupling between sdk snapshots and the APEX runtime. It cannot
+eliminate that coupling completely though. Doing so may require changes to the
+code.
+
+This tool provides support for managing those properties but it cannot decide
+whether the set of package prefixes suggested is appropriate that needs the
+input of the developer.
+
+Please run the following command:
+ m {signature_patterns_files}
+
+And then check the '{signature_patterns_files}' for any mention of
+implementation classes and packages (i.e. those classes/packages that do not
+contain any part of an API surface, including the hidden API). If they are
+found then the code should ideally be moved to a package unique to this module
+that is contained within a package that is part of an API surface.
+
+The format of the file is a list of patterns:
+
+* Patterns for split packages will list every class in that package.
+
+* Patterns for package prefixes will end with .../**.
+
+* Patterns for packages which are not split but cannot use a package prefix
+because there are sub-packages which are provided by another module will end
+with .../*.
+""")
+
+ def compute_hiddenapi_package_properties(self):
+ trie = signature_trie()
+ # Populate the trie with the classes that are provided by the
+ # bootclasspath_fragment tagging them to make it clear where they
+ # are from.
+ sorted_classes = sorted(self.classes)
+ for class_name in sorted_classes:
+ trie.add(class_name + _FAKE_MEMBER, ClassProvider.BCPF)
+
+ monolithic_classes = set()
+ abs_flags_file = os.path.join(self.top_dir, _FLAGS_FILE)
+ with open(abs_flags_file, "r", encoding="utf8") as f:
+ for line in iter(f.readline, ""):
+ signature = self.line_to_signature(line)
+ class_name = self.signature_to_class(signature)
+ if (class_name not in monolithic_classes and
+ class_name not in self.classes):
+ trie.add(
+ class_name + _FAKE_MEMBER,
+ ClassProvider.OTHER,
+ only_if_matches=True)
+ monolithic_classes.add(class_name)
+
+ split_packages = []
+ single_packages = []
+ package_prefixes = []
+ self.recurse_hiddenapi_packages_trie(trie, split_packages,
+ single_packages, package_prefixes)
+ return split_packages, single_packages, package_prefixes
+
+ def recurse_hiddenapi_packages_trie(self, node, split_packages,
+ single_packages, package_prefixes):
+ nodes = node.child_nodes()
+ if nodes:
+ for child in nodes:
+ # Ignore any non-package nodes.
+ if child.type != "package":
+ continue
+
+ package = child.selector.replace("/", ".")
+
+ providers = set(child.get_matching_rows("**"))
+ if not providers:
+ # The package and all its sub packages contain no
+ # classes. This should never happen.
+ pass
+ elif providers == {ClassProvider.BCPF}:
+ # The package and all its sub packages only contain
+ # classes provided by the bootclasspath_fragment.
+ logging.debug("Package '%s.**' is not split", package)
+ package_prefixes.append(package)
+ # There is no point traversing into the sub packages.
+ continue
+ elif providers == {ClassProvider.OTHER}:
+ # The package and all its sub packages contain no
+ # classes provided by the bootclasspath_fragment.
+ # There is no point traversing into the sub packages.
+ logging.debug("Package '%s.**' contains no classes from %s",
+ package, self.bcpf)
+ continue
+ elif ClassProvider.BCPF in providers:
+ # The package and all its sub packages contain classes
+ # provided by the bootclasspath_fragment and other
+ # sources.
+ logging.debug(
+ "Package '%s.**' contains classes from "
+ "%s and other sources", package, self.bcpf)
+
+ providers = set(child.get_matching_rows("*"))
+ if not providers:
+ # The package contains no classes.
+ logging.debug("Package: %s contains no classes", package)
+ elif providers == {ClassProvider.BCPF}:
+ # The package only contains classes provided by the
+ # bootclasspath_fragment.
+ logging.debug("Package '%s.*' is not split", package)
+ single_packages.append(package)
+ elif providers == {ClassProvider.OTHER}:
+ # The package contains no classes provided by the
+ # bootclasspath_fragment. Child nodes make contain such
+ # classes.
+ logging.debug("Package '%s.*' contains no classes from %s",
+ package, self.bcpf)
+ elif ClassProvider.BCPF in providers:
+ # The package contains classes provided by both the
+ # bootclasspath_fragment and some other source.
+ logging.debug("Package '%s.*' is split", package)
+ split_packages.append(package)
+
+ self.recurse_hiddenapi_packages_trie(child, split_packages,
+ single_packages,
+ package_prefixes)
+
+
+def newline_stripping_iter(iterator):
+ """Return an iterator over the iterator that strips trailing white space."""
+ lines = iter(iterator, "")
+ lines = (line.rstrip() for line in lines)
+ return lines
+
+
+def format_comment_as_text(text, indent):
+ return "".join(
+ [f"{line}\n" for line in format_comment_as_lines(text, indent)])
+
+
+def format_comment_as_lines(text, indent):
+ lines = textwrap.wrap(text.strip("\n"), width=77 - len(indent))
+ lines = [f"{indent}// {line}" for line in lines]
+ return lines
+
+
+def log_stream_for_subprocess():
+ stream = subprocess.DEVNULL
+ for handler in logging.root.handlers:
+ if handler.level == logging.DEBUG:
+ if isinstance(handler, logging.StreamHandler):
+ stream = handler.stream
+ return stream
+
+
+def main(argv):
+ args_parser = argparse.ArgumentParser(
+ description="Analyze a bootclasspath_fragment module.")
+ args_parser.add_argument(
+ "--bcpf",
+ help="The bootclasspath_fragment module to analyze",
+ required=True,
+ )
+ args_parser.add_argument(
+ "--apex",
+ help="The apex module to which the bootclasspath_fragment belongs. It "
+ "is not strictly necessary at the moment but providing it will "
+ "allow this script to give more useful messages and it may be"
+ "required in future.",
+ default="SPECIFY-APEX-OPTION")
+ args_parser.add_argument(
+ "--sdk",
+ help="The sdk module to which the bootclasspath_fragment belongs. It "
+ "is not strictly necessary at the moment but providing it will "
+ "allow this script to give more useful messages and it may be"
+ "required in future.",
+ default="SPECIFY-SDK-OPTION")
+ args_parser.add_argument(
+ "--fix",
+ help="Attempt to fix any issues found automatically.",
+ action="store_true",
+ default=False)
+ args = args_parser.parse_args(argv[1:])
+ top_dir = os.environ["ANDROID_BUILD_TOP"] + "/"
+ out_dir = os.environ.get("OUT_DIR", os.path.join(top_dir, "out"))
+ product_out_dir = os.environ.get("ANDROID_PRODUCT_OUT", top_dir)
+ # Make product_out_dir relative to the top so it can be used as part of a
+ # build target.
+ product_out_dir = product_out_dir.removeprefix(top_dir)
+ log_fd, abs_log_file = tempfile.mkstemp(
+ suffix="_analyze_bcpf.log", text=True)
+
+ with os.fdopen(log_fd, "w") as log_file:
+ # Set up debug logging to the log file.
+ logging.basicConfig(
+ level=logging.DEBUG,
+ format="%(levelname)-8s %(message)s",
+ stream=log_file)
+
+ # define a Handler which writes INFO messages or higher to the
+ # sys.stdout with just the message.
+ console = logging.StreamHandler()
+ console.setLevel(logging.INFO)
+ console.setFormatter(logging.Formatter("%(message)s"))
+ # add the handler to the root logger
+ logging.getLogger("").addHandler(console)
+
+ print(f"Writing log to {abs_log_file}")
+ try:
+ analyzer = BcpfAnalyzer(
+ tool_path=argv[0],
+ top_dir=top_dir,
+ out_dir=out_dir,
+ product_out_dir=product_out_dir,
+ bcpf=args.bcpf,
+ apex=args.apex,
+ sdk=args.sdk,
+ fix=args.fix,
+ )
+ analyzer.analyze()
+ finally:
+ print(f"Log written to {abs_log_file}")
+
+
+if __name__ == "__main__":
+ main(sys.argv)
diff --git a/scripts/hiddenapi/analyze_bcpf_test.py b/scripts/hiddenapi/analyze_bcpf_test.py
new file mode 100644
index 0000000..650dd54
--- /dev/null
+++ b/scripts/hiddenapi/analyze_bcpf_test.py
@@ -0,0 +1,650 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2022 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.
+"""Unit tests for analyzing bootclasspath_fragment modules."""
+import os.path
+import shutil
+import tempfile
+import unittest
+import unittest.mock
+
+import sys
+
+import analyze_bcpf as ab
+
+_FRAMEWORK_HIDDENAPI = "frameworks/base/boot/hiddenapi"
+_MAX_TARGET_O = f"{_FRAMEWORK_HIDDENAPI}/hiddenapi-max-target-o.txt"
+_MAX_TARGET_P = f"{_FRAMEWORK_HIDDENAPI}/hiddenapi-max-target-p.txt"
+_MAX_TARGET_Q = f"{_FRAMEWORK_HIDDENAPI}/hiddenapi-max-target-q.txt"
+_MAX_TARGET_R = f"{_FRAMEWORK_HIDDENAPI}/hiddenapi-max-target-r-loprio.txt"
+
+_MULTI_LINE_COMMENT = """
+Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut arcu justo,
+bibendum eu malesuada vel, fringilla in odio. Etiam gravida ultricies sem
+tincidunt luctus.""".replace("\n", " ").strip()
+
+
+class FakeBuildOperation(ab.BuildOperation):
+
+ def __init__(self, lines, return_code):
+ ab.BuildOperation.__init__(self, None)
+ self._lines = lines
+ self.returncode = return_code
+
+ def lines(self):
+ return iter(self._lines)
+
+ def wait(self, *args, **kwargs):
+ return
+
+
+class TestAnalyzeBcpf(unittest.TestCase):
+
+ def setUp(self):
+ # Create a temporary directory
+ self.test_dir = tempfile.mkdtemp()
+
+ def tearDown(self):
+ # Remove the directory after the test
+ shutil.rmtree(self.test_dir)
+
+ @staticmethod
+ def write_abs_file(abs_path, contents):
+ os.makedirs(os.path.dirname(abs_path), exist_ok=True)
+ with open(abs_path, "w", encoding="utf8") as f:
+ print(contents.removeprefix("\n"), file=f, end="")
+
+ def populate_fs(self, fs):
+ for path, contents in fs.items():
+ abs_path = os.path.join(self.test_dir, path)
+ self.write_abs_file(abs_path, contents)
+
+ def create_analyzer_for_test(self,
+ fs=None,
+ bcpf="bcpf",
+ apex="apex",
+ sdk="sdk",
+ fix=False):
+ if fs:
+ self.populate_fs(fs)
+
+ top_dir = self.test_dir
+ out_dir = os.path.join(self.test_dir, "out")
+ product_out_dir = "out/product"
+
+ bcpf_dir = f"{bcpf}-dir"
+ modules = {bcpf: {"path": [bcpf_dir]}}
+ module_info = ab.ModuleInfo(modules)
+
+ analyzer = ab.BcpfAnalyzer(
+ tool_path=os.path.join(out_dir, "bin"),
+ top_dir=top_dir,
+ out_dir=out_dir,
+ product_out_dir=product_out_dir,
+ bcpf=bcpf,
+ apex=apex,
+ sdk=sdk,
+ fix=fix,
+ module_info=module_info,
+ )
+ analyzer.load_all_flags()
+ return analyzer
+
+ def test_reformat_report_text(self):
+ lines = """
+99. An item in a numbered list
+that traverses multiple lines.
+
+ An indented example
+ that should not be reformatted.
+"""
+ reformatted = ab.BcpfAnalyzer.reformat_report_test(lines)
+ self.assertEqual(
+ """
+99. An item in a numbered list that traverses multiple lines.
+
+ An indented example
+ that should not be reformatted.
+""", reformatted)
+
+ def do_test_build_flags(self, fix):
+ lines = """
+ERROR: Hidden API flags are inconsistent:
+< out/soong/.intermediates/bcpf-dir/bcpf-dir/filtered-flags.csv
+> out/soong/hiddenapi/hiddenapi-flags.csv
+
+< Lacme/test/Class;-><init>()V,blocked
+> Lacme/test/Class;-><init>()V,max-target-o
+
+< Lacme/test/Other;->getThing()Z,blocked
+> Lacme/test/Other;->getThing()Z,max-target-p
+
+< Lacme/test/Widget;-><init()V,blocked
+> Lacme/test/Widget;-><init()V,max-target-q
+
+< Lacme/test/Gadget;->NAME:Ljava/lang/String;,blocked
+> Lacme/test/Gadget;->NAME:Ljava/lang/String;,lo-prio,max-target-r
+16:37:32 ninja failed with: exit status 1
+""".strip().splitlines()
+ operation = FakeBuildOperation(lines=lines, return_code=1)
+
+ fs = {
+ _MAX_TARGET_O:
+ """
+Lacme/items/Magnet;->size:I
+Lacme/test/Class;-><init>()V
+""",
+ _MAX_TARGET_P:
+ """
+Lacme/items/Rocket;->size:I
+Lacme/test/Other;->getThing()Z
+""",
+ _MAX_TARGET_Q:
+ """
+Lacme/items/Rock;->size:I
+Lacme/test/Widget;-><init()V
+""",
+ _MAX_TARGET_R:
+ """
+Lacme/items/Lever;->size:I
+Lacme/test/Gadget;->NAME:Ljava/lang/String;
+""",
+ "bcpf-dir/hiddenapi/hiddenapi-max-target-p.txt":
+ """
+Lacme/old/Class;->getWidget()Lacme/test/Widget;
+""",
+ "out/soong/.intermediates/bcpf-dir/bcpf/all-flags.csv":
+ """
+Lacme/test/Gadget;->NAME:Ljava/lang/String;,blocked
+Lacme/test/Widget;-><init()V,blocked
+Lacme/test/Class;-><init>()V,blocked
+Lacme/test/Other;->getThing()Z,blocked
+""",
+ }
+
+ analyzer = self.create_analyzer_for_test(fs, fix=fix)
+
+ # Override the build_file_read_output() method to just return a fake
+ # build operation.
+ analyzer.build_file_read_output = unittest.mock.Mock(
+ return_value=operation)
+
+ # Override the run_command() method to do nothing.
+ analyzer.run_command = unittest.mock.Mock()
+
+ result = ab.Result()
+
+ analyzer.build_monolithic_flags(result)
+ expected_diffs = {
+ "Lacme/test/Gadget;->NAME:Ljava/lang/String;":
+ (["blocked"], ["lo-prio", "max-target-r"]),
+ "Lacme/test/Widget;-><init()V": (["blocked"], ["max-target-q"]),
+ "Lacme/test/Class;-><init>()V": (["blocked"], ["max-target-o"]),
+ "Lacme/test/Other;->getThing()Z": (["blocked"], ["max-target-p"])
+ }
+ self.assertEqual(expected_diffs, result.diffs, msg="flag differences")
+
+ expected_property_changes = [
+ ab.HiddenApiPropertyChange(
+ property_name="max_target_o_low_priority",
+ values=["hiddenapi/hiddenapi-max-target-o-low-priority.txt"],
+ property_comment=""),
+ ab.HiddenApiPropertyChange(
+ property_name="max_target_p",
+ values=["hiddenapi/hiddenapi-max-target-p.txt"],
+ property_comment=""),
+ ab.HiddenApiPropertyChange(
+ property_name="max_target_q",
+ values=["hiddenapi/hiddenapi-max-target-q.txt"],
+ property_comment=""),
+ ab.HiddenApiPropertyChange(
+ property_name="max_target_r_low_priority",
+ values=["hiddenapi/hiddenapi-max-target-r-low-priority.txt"],
+ property_comment=""),
+ ]
+ self.assertEqual(
+ expected_property_changes,
+ result.property_changes,
+ msg="property changes")
+
+ return result
+
+ def test_build_flags_report(self):
+ result = self.do_test_build_flags(fix=False)
+
+ expected_file_changes = [
+ ab.FileChange(
+ path="bcpf-dir/hiddenapi/"
+ "hiddenapi-max-target-o-low-priority.txt",
+ description="""Add the following entries:
+ Lacme/test/Class;-><init>()V
+""",
+ ),
+ ab.FileChange(
+ path="bcpf-dir/hiddenapi/hiddenapi-max-target-p.txt",
+ description="""Add the following entries:
+ Lacme/test/Other;->getThing()Z
+""",
+ ),
+ ab.FileChange(
+ path="bcpf-dir/hiddenapi/hiddenapi-max-target-q.txt",
+ description="""Add the following entries:
+ Lacme/test/Widget;-><init()V
+"""),
+ ab.FileChange(
+ path="bcpf-dir/hiddenapi/"
+ "hiddenapi-max-target-r-low-priority.txt",
+ description="""Add the following entries:
+ Lacme/test/Gadget;->NAME:Ljava/lang/String;
+"""),
+ ab.FileChange(
+ path="frameworks/base/boot/hiddenapi/"
+ "hiddenapi-max-target-o.txt",
+ description="""Remove the following entries:
+ Lacme/test/Class;-><init>()V
+"""),
+ ab.FileChange(
+ path="frameworks/base/boot/hiddenapi/"
+ "hiddenapi-max-target-p.txt",
+ description="""Remove the following entries:
+ Lacme/test/Other;->getThing()Z
+"""),
+ ab.FileChange(
+ path="frameworks/base/boot/hiddenapi/"
+ "hiddenapi-max-target-q.txt",
+ description="""Remove the following entries:
+ Lacme/test/Widget;-><init()V
+"""),
+ ab.FileChange(
+ path="frameworks/base/boot/hiddenapi/"
+ "hiddenapi-max-target-r-loprio.txt",
+ description="""Remove the following entries:
+ Lacme/test/Gadget;->NAME:Ljava/lang/String;
+""")
+ ]
+ result.file_changes.sort()
+ self.assertEqual(
+ expected_file_changes, result.file_changes, msg="file_changes")
+
+ def test_build_flags_fix(self):
+ result = self.do_test_build_flags(fix=True)
+
+ expected_file_changes = [
+ ab.FileChange(
+ path="bcpf-dir/hiddenapi/"
+ "hiddenapi-max-target-o-low-priority.txt",
+ description="Created with 'bcpf' specific entries"),
+ ab.FileChange(
+ path="bcpf-dir/hiddenapi/hiddenapi-max-target-p.txt",
+ description="Added 'bcpf' specific entries"),
+ ab.FileChange(
+ path="bcpf-dir/hiddenapi/hiddenapi-max-target-q.txt",
+ description="Created with 'bcpf' specific entries"),
+ ab.FileChange(
+ path="bcpf-dir/hiddenapi/"
+ "hiddenapi-max-target-r-low-priority.txt",
+ description="Created with 'bcpf' specific entries"),
+ ab.FileChange(
+ path=_MAX_TARGET_O,
+ description="Removed 'bcpf' specific entries"),
+ ab.FileChange(
+ path=_MAX_TARGET_P,
+ description="Removed 'bcpf' specific entries"),
+ ab.FileChange(
+ path=_MAX_TARGET_Q,
+ description="Removed 'bcpf' specific entries"),
+ ab.FileChange(
+ path=_MAX_TARGET_R,
+ description="Removed 'bcpf' specific entries")
+ ]
+
+ result.file_changes.sort()
+ self.assertEqual(
+ expected_file_changes, result.file_changes, msg="file_changes")
+
+ expected_file_contents = {
+ "bcpf-dir/hiddenapi/hiddenapi-max-target-o-low-priority.txt":
+ """
+Lacme/test/Class;-><init>()V
+""",
+ "bcpf-dir/hiddenapi/hiddenapi-max-target-p.txt":
+ """
+Lacme/old/Class;->getWidget()Lacme/test/Widget;
+Lacme/test/Other;->getThing()Z
+""",
+ "bcpf-dir/hiddenapi/hiddenapi-max-target-q.txt":
+ """
+Lacme/test/Widget;-><init()V
+""",
+ "bcpf-dir/hiddenapi/hiddenapi-max-target-r-low-priority.txt":
+ """
+Lacme/test/Gadget;->NAME:Ljava/lang/String;
+""",
+ _MAX_TARGET_O:
+ """
+Lacme/items/Magnet;->size:I
+""",
+ _MAX_TARGET_P:
+ """
+Lacme/items/Rocket;->size:I
+""",
+ _MAX_TARGET_Q:
+ """
+Lacme/items/Rock;->size:I
+""",
+ _MAX_TARGET_R:
+ """
+Lacme/items/Lever;->size:I
+""",
+ }
+ for file_change in result.file_changes:
+ path = file_change.path
+ expected_contents = expected_file_contents[path].lstrip()
+ abs_path = os.path.join(self.test_dir, path)
+ with open(abs_path, "r", encoding="utf8") as tio:
+ contents = tio.read()
+ self.assertEqual(
+ expected_contents, contents, msg=f"{path} contents")
+
+ def test_compute_hiddenapi_package_properties(self):
+ fs = {
+ "out/soong/.intermediates/bcpf-dir/bcpf/all-flags.csv":
+ """
+La/b/C;->m()V
+La/b/c/D;->m()V
+La/b/c/E;->m()V
+Lb/c/D;->m()V
+Lb/c/E;->m()V
+Lb/c/d/E;->m()V
+""",
+ "out/soong/hiddenapi/hiddenapi-flags.csv":
+ """
+La/b/C;->m()V
+La/b/D;->m()V
+La/b/E;->m()V
+La/b/c/D;->m()V
+La/b/c/E;->m()V
+La/b/c/d/E;->m()V
+Lb/c/D;->m()V
+Lb/c/E;->m()V
+Lb/c/d/E;->m()V
+"""
+ }
+ analyzer = self.create_analyzer_for_test(fs)
+ analyzer.load_all_flags()
+
+ split_packages, single_packages, package_prefixes = \
+ analyzer.compute_hiddenapi_package_properties()
+ self.assertEqual(["a.b"], split_packages)
+ self.assertEqual(["a.b.c"], single_packages)
+ self.assertEqual(["b"], package_prefixes)
+
+
+class TestHiddenApiPropertyChange(unittest.TestCase):
+
+ def setUp(self):
+ # Create a temporary directory
+ self.test_dir = tempfile.mkdtemp()
+
+ def tearDown(self):
+ # Remove the directory after the test
+ shutil.rmtree(self.test_dir)
+
+ def check_change_fix(self, change, bpmodify_output, expected):
+ file = os.path.join(self.test_dir, "Android.bp")
+
+ with open(file, "w", encoding="utf8") as tio:
+ tio.write(bpmodify_output.strip("\n"))
+
+ bpmodify_runner = ab.BpModifyRunner(
+ os.path.join(os.path.dirname(sys.argv[0]), "bpmodify"))
+ change.fix_bp_file(file, "bcpf", bpmodify_runner)
+
+ with open(file, "r", encoding="utf8") as tio:
+ contents = tio.read()
+ self.assertEqual(expected.lstrip("\n"), contents)
+
+ def check_change_snippet(self, change, expected):
+ snippet = change.snippet(" ")
+ self.assertEqual(expected, snippet)
+
+ def test_change_property_with_value_no_comment(self):
+ change = ab.HiddenApiPropertyChange(
+ property_name="split_packages",
+ values=["android.provider"],
+ )
+
+ self.check_change_snippet(
+ change, """
+ split_packages: [
+ "android.provider",
+ ],
+""")
+
+ self.check_change_fix(
+ change, """
+bootclasspath_fragment {
+ name: "bcpf",
+
+ // modified by the Soong or platform compat team.
+ hidden_api: {
+ max_target_o_low_priority: ["hiddenapi/hiddenapi-max-target-o-low-priority.txt"],
+ split_packages: [
+ "android.provider",
+ ],
+ },
+}
+""", """
+bootclasspath_fragment {
+ name: "bcpf",
+
+ // modified by the Soong or platform compat team.
+ hidden_api: {
+ max_target_o_low_priority: ["hiddenapi/hiddenapi-max-target-o-low-priority.txt"],
+ split_packages: [
+ "android.provider",
+ ],
+ },
+}
+""")
+
+ def test_change_property_with_value_and_comment(self):
+ change = ab.HiddenApiPropertyChange(
+ property_name="split_packages",
+ values=["android.provider"],
+ property_comment=_MULTI_LINE_COMMENT,
+ )
+
+ self.check_change_snippet(
+ change, """
+ // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut arcu
+ // justo, bibendum eu malesuada vel, fringilla in odio. Etiam gravida
+ // ultricies sem tincidunt luctus.
+ split_packages: [
+ "android.provider",
+ ],
+""")
+
+ self.check_change_fix(
+ change, """
+bootclasspath_fragment {
+ name: "bcpf",
+
+ // modified by the Soong or platform compat team.
+ hidden_api: {
+ max_target_o_low_priority: ["hiddenapi/hiddenapi-max-target-o-low-priority.txt"],
+ split_packages: [
+ "android.provider",
+ ],
+
+ single_packages: [
+ "android.system",
+ ],
+
+ },
+}
+""", """
+bootclasspath_fragment {
+ name: "bcpf",
+
+ // modified by the Soong or platform compat team.
+ hidden_api: {
+ max_target_o_low_priority: ["hiddenapi/hiddenapi-max-target-o-low-priority.txt"],
+
+ // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut arcu
+ // justo, bibendum eu malesuada vel, fringilla in odio. Etiam gravida
+ // ultricies sem tincidunt luctus.
+ split_packages: [
+ "android.provider",
+ ],
+
+ single_packages: [
+ "android.system",
+ ],
+
+ },
+}
+""")
+
+ def test_set_property_with_value_and_comment(self):
+ change = ab.HiddenApiPropertyChange(
+ property_name="split_packages",
+ values=["another.provider", "other.system"],
+ property_comment=_MULTI_LINE_COMMENT,
+ action=ab.PropertyChangeAction.REPLACE,
+ )
+
+ self.check_change_snippet(
+ change, """
+ // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut arcu
+ // justo, bibendum eu malesuada vel, fringilla in odio. Etiam gravida
+ // ultricies sem tincidunt luctus.
+ split_packages: [
+ "another.provider",
+ "other.system",
+ ],
+""")
+
+ self.check_change_fix(
+ change, """
+bootclasspath_fragment {
+ name: "bcpf",
+
+ // modified by the Soong or platform compat team.
+ hidden_api: {
+ max_target_o_low_priority: ["hiddenapi/hiddenapi-max-target-o-low-priority.txt"],
+ split_packages: [
+ "another.provider",
+ "other.system",
+ ],
+ },
+}
+""", """
+bootclasspath_fragment {
+ name: "bcpf",
+
+ // modified by the Soong or platform compat team.
+ hidden_api: {
+ max_target_o_low_priority: ["hiddenapi/hiddenapi-max-target-o-low-priority.txt"],
+
+ // Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut arcu
+ // justo, bibendum eu malesuada vel, fringilla in odio. Etiam gravida
+ // ultricies sem tincidunt luctus.
+ split_packages: [
+ "another.provider",
+ "other.system",
+ ],
+ },
+}
+""")
+
+ def test_set_property_with_no_value_or_comment(self):
+ change = ab.HiddenApiPropertyChange(
+ property_name="split_packages",
+ values=[],
+ action=ab.PropertyChangeAction.REPLACE,
+ )
+
+ self.check_change_snippet(change, """
+ split_packages: [],
+""")
+
+ self.check_change_fix(
+ change, """
+bootclasspath_fragment {
+ name: "bcpf",
+
+ // modified by the Soong or platform compat team.
+ hidden_api: {
+ max_target_o_low_priority: ["hiddenapi/hiddenapi-max-target-o-low-priority.txt"],
+ split_packages: [
+ "another.provider",
+ "other.system",
+ ],
+ package_prefixes: ["android.provider"],
+ },
+}
+""", """
+bootclasspath_fragment {
+ name: "bcpf",
+
+ // modified by the Soong or platform compat team.
+ hidden_api: {
+ max_target_o_low_priority: ["hiddenapi/hiddenapi-max-target-o-low-priority.txt"],
+ split_packages: [],
+ package_prefixes: ["android.provider"],
+ },
+}
+""")
+
+ def test_set_empty_property_with_no_value_or_comment(self):
+ change = ab.HiddenApiPropertyChange(
+ property_name="split_packages",
+ values=[],
+ action=ab.PropertyChangeAction.REPLACE,
+ )
+
+ self.check_change_snippet(change, """
+ split_packages: [],
+""")
+
+ self.check_change_fix(
+ change, """
+bootclasspath_fragment {
+ name: "bcpf",
+
+ // modified by the Soong or platform compat team.
+ hidden_api: {
+ max_target_o_low_priority: ["hiddenapi/hiddenapi-max-target-o-low-priority.txt"],
+ split_packages: [],
+ package_prefixes: ["android.provider"],
+ },
+}
+""", """
+bootclasspath_fragment {
+ name: "bcpf",
+
+ // modified by the Soong or platform compat team.
+ hidden_api: {
+ max_target_o_low_priority: ["hiddenapi/hiddenapi-max-target-o-low-priority.txt"],
+ split_packages: [],
+ package_prefixes: ["android.provider"],
+ },
+}
+""")
+
+
+if __name__ == "__main__":
+ unittest.main(verbosity=3)
diff --git a/scripts/microfactory.bash b/scripts/microfactory.bash
index 5e702e0..192b38f 100644
--- a/scripts/microfactory.bash
+++ b/scripts/microfactory.bash
@@ -59,7 +59,7 @@
BUILDDIR=$(getoutdir) \
SRCDIR=${TOP} \
BLUEPRINTDIR=${TOP}/build/blueprint \
- EXTRA_ARGS="-pkg-path android/soong=${TOP}/build/soong -pkg-path google.golang.org/protobuf=${TOP}/external/golang-protobuf" \
+ EXTRA_ARGS="-pkg-path android/soong=${TOP}/build/soong -pkg-path rbcrun=${TOP}/build/make/tools/rbcrun -pkg-path google.golang.org/protobuf=${TOP}/external/golang-protobuf -pkg-path go.starlark.net=${TOP}/external/starlark-go" \
build_go $@
}
diff --git a/scripts/rbc-run b/scripts/rbc-run
index b8a6c0c..8d93f0e 100755
--- a/scripts/rbc-run
+++ b/scripts/rbc-run
@@ -6,8 +6,8 @@
set -eu
declare -r output_root="${OUT_DIR:-out}"
-declare -r runner="${output_root}/soong/rbcrun"
-declare -r converter="${output_root}/soong/mk2rbc"
+declare -r runner="${output_root}/rbcrun"
+declare -r converter="${output_root}/mk2rbc"
declare -r launcher="${output_root}/rbc/launcher.rbc"
declare -r makefile_list="${output_root}/.module_paths/configuration.list"
declare -r makefile="$1"
diff --git a/snapshot/host_fake_snapshot.go b/snapshot/host_fake_snapshot.go
index 6b4e12b..b04657d 100644
--- a/snapshot/host_fake_snapshot.go
+++ b/snapshot/host_fake_snapshot.go
@@ -68,6 +68,12 @@
registerHostSnapshotComponents(android.InitRegistrationContext)
}
+// Add prebuilt information to snapshot data
+type hostSnapshotFakeJsonFlags struct {
+ SnapshotJsonFlags
+ Prebuilt bool `json:",omitempty"`
+}
+
func registerHostSnapshotComponents(ctx android.RegistrationContext) {
ctx.RegisterSingletonType("host-fake-snapshot", HostToolsFakeAndroidSingleton)
}
@@ -94,7 +100,9 @@
// Find all host binary modules add 'fake' versions to snapshot
var outputs android.Paths
seen := make(map[string]bool)
- var jsonData []SnapshotJsonFlags
+ var jsonData []hostSnapshotFakeJsonFlags
+ prebuilts := make(map[string]bool)
+
ctx.VisitAllModules(func(module android.Module) {
if module.Target().Os != ctx.Config().BuildOSTarget.Os {
return
@@ -104,9 +112,10 @@
}
if android.IsModulePrebuilt(module) {
+ // Add non-prebuilt module name to map of prebuilts
+ prebuilts[android.RemoveOptionalPrebuiltPrefix(module.Name())] = true
return
}
-
if !module.Enabled() || module.IsHideFromMake() {
return
}
@@ -114,17 +123,23 @@
if !apexInfo.IsForPlatform() {
return
}
- path := hostBinToolPath(module)
+ path := hostToolPath(module)
if path.Valid() && path.String() != "" {
outFile := filepath.Join(c.snapshotDir, path.String())
if !seen[outFile] {
seen[outFile] = true
outputs = append(outputs, WriteStringToFileRule(ctx, "", outFile))
- jsonData = append(jsonData, *hostBinJsonDesc(module))
+ jsonData = append(jsonData, hostSnapshotFakeJsonFlags{*hostJsonDesc(module), false})
}
}
})
-
+ // Update any module prebuilt information
+ for idx, _ := range jsonData {
+ if _, ok := prebuilts[jsonData[idx].ModuleName]; ok {
+ // Prebuilt exists for this module
+ jsonData[idx].Prebuilt = true
+ }
+ }
marsh, err := json.Marshal(jsonData)
if err != nil {
ctx.Errorf("host fake snapshot json marshal failure: %#v", err)
diff --git a/snapshot/host_snapshot.go b/snapshot/host_snapshot.go
index 09a382e..9793218 100644
--- a/snapshot/host_snapshot.go
+++ b/snapshot/host_snapshot.go
@@ -19,6 +19,7 @@
"fmt"
"path/filepath"
"sort"
+ "strings"
"github.com/google/blueprint"
"github.com/google/blueprint/proptools"
@@ -62,6 +63,11 @@
installDir android.InstallPath
}
+type ProcMacro interface {
+ ProcMacro() bool
+ CrateName() string
+}
+
func hostSnapshotFactory() android.Module {
module := &hostSnapshot{}
initHostToolsModule(module)
@@ -94,7 +100,7 @@
// Create JSON file based on the direct dependencies
ctx.VisitDirectDeps(func(dep android.Module) {
- desc := hostBinJsonDesc(dep)
+ desc := hostJsonDesc(dep)
if desc != nil {
jsonData = append(jsonData, *desc)
}
@@ -145,7 +151,7 @@
f.installDir = android.PathForModuleInstall(ctx)
- f.CopyDepsToZip(ctx, depsZipFile)
+ f.CopyDepsToZip(ctx, f.GatherPackagingSpecs(ctx), depsZipFile)
builder := android.NewRuleBuilder(pctx, ctx)
builder.Command().
@@ -183,7 +189,7 @@
}
// Get host tools path and relative install string helpers
-func hostBinToolPath(m android.Module) android.OptionalPath {
+func hostToolPath(m android.Module) android.OptionalPath {
if provider, ok := m.(android.HostToolProvider); ok {
return provider.HostToolPath()
}
@@ -198,18 +204,30 @@
return outString
}
-// Create JSON description for given module, only create descriptions for binary modueles which
-// provide a valid HostToolPath
-func hostBinJsonDesc(m android.Module) *SnapshotJsonFlags {
- path := hostBinToolPath(m)
+// Create JSON description for given module, only create descriptions for binary modules
+// and rust_proc_macro modules which provide a valid HostToolPath
+func hostJsonDesc(m android.Module) *SnapshotJsonFlags {
+ path := hostToolPath(m)
relPath := hostRelativePathString(m)
+ procMacro := false
+ moduleStem := filepath.Base(path.String())
+ crateName := ""
+
+ if pm, ok := m.(ProcMacro); ok && pm.ProcMacro() {
+ procMacro = pm.ProcMacro()
+ moduleStem = strings.TrimSuffix(moduleStem, filepath.Ext(moduleStem))
+ crateName = pm.CrateName()
+ }
+
if path.Valid() && path.String() != "" {
return &SnapshotJsonFlags{
ModuleName: m.Name(),
- ModuleStemName: filepath.Base(path.String()),
+ ModuleStemName: moduleStem,
Filename: path.String(),
Required: append(m.HostRequiredModuleNames(), m.RequiredModuleNames()...),
RelativeInstallPath: relPath,
+ RustProcMacro: procMacro,
+ CrateName: crateName,
}
}
return nil
diff --git a/snapshot/snapshot_base.go b/snapshot/snapshot_base.go
index 79d3cf6..4a14f2e 100644
--- a/snapshot/snapshot_base.go
+++ b/snapshot/snapshot_base.go
@@ -114,6 +114,8 @@
RelativeInstallPath string `json:",omitempty"`
Filename string `json:",omitempty"`
ModuleStemName string `json:",omitempty"`
+ RustProcMacro bool `json:",omitempty"`
+ CrateName string `json:",omitempty"`
// dependencies
Required []string `json:",omitempty"`
diff --git a/soong_ui.bash b/soong_ui.bash
index c1c236b..49c4b78 100755
--- a/soong_ui.bash
+++ b/soong_ui.bash
@@ -53,6 +53,8 @@
source ${TOP}/build/soong/scripts/microfactory.bash
soong_build_go soong_ui android/soong/cmd/soong_ui
+soong_build_go mk2rbc android/soong/mk2rbc/cmd
+soong_build_go rbcrun rbcrun/cmd
cd ${TOP}
exec "$(getoutdir)/soong_ui" "$@"
diff --git a/tests/lib.sh b/tests/lib.sh
index 55f9ab4..1bb2df9 100644
--- a/tests/lib.sh
+++ b/tests/lib.sh
@@ -83,12 +83,14 @@
function create_mock_soong {
copy_directory build/blueprint
copy_directory build/soong
+ copy_directory build/make/tools/rbcrun
symlink_directory prebuilts/go
symlink_directory prebuilts/build-tools
symlink_directory prebuilts/clang/host
symlink_directory external/go-cmp
symlink_directory external/golang-protobuf
+ symlink_directory external/starlark-go
touch "$MOCK_TOP/Android.bp"
}
diff --git a/ui/build/dumpvars.go b/ui/build/dumpvars.go
index 3f10f75..11311f9 100644
--- a/ui/build/dumpvars.go
+++ b/ui/build/dumpvars.go
@@ -262,12 +262,6 @@
"BUILD_BROKEN_USES_BUILD_STATIC_LIBRARY",
}, exportEnvVars...), BannerVars...)
- // We need Roboleaf converter and runner in the mixed mode
- runMicrofactory(ctx, config, "mk2rbc", "android/soong/mk2rbc/cmd",
- map[string]string{"android/soong": "build/soong"})
- runMicrofactory(ctx, config, "rbcrun", "rbcrun/cmd",
- map[string]string{"go.starlark.net": "external/starlark-go", "rbcrun": "build/make/tools/rbcrun"})
-
makeVars, err := dumpMakeVars(ctx, config, config.Arguments(), allVars, true, "")
if err != nil {
ctx.Fatalln("Error dumping make vars:", err)
diff --git a/ui/build/soong.go b/ui/build/soong.go
index 1b993e1..c7f22f9 100644
--- a/ui/build/soong.go
+++ b/ui/build/soong.go
@@ -537,7 +537,7 @@
}
func loadSoongBuildMetrics(ctx Context, config Config) *soong_metrics_proto.SoongBuildMetrics {
- soongBuildMetricsFile := filepath.Join(config.OutDir(), "soong", "soong_build_metrics.pb")
+ soongBuildMetricsFile := filepath.Join(config.LogsDir(), "soong_build_metrics.pb")
buf, err := ioutil.ReadFile(soongBuildMetricsFile)
if err != nil {
ctx.Fatalf("Failed to load %s: %s", soongBuildMetricsFile, err)
diff --git a/ui/metrics/bp2build_metrics_proto/bp2build_metrics.pb.go b/ui/metrics/bp2build_metrics_proto/bp2build_metrics.pb.go
index 95f02ca..93f3471 100644
--- a/ui/metrics/bp2build_metrics_proto/bp2build_metrics.pb.go
+++ b/ui/metrics/bp2build_metrics_proto/bp2build_metrics.pb.go
@@ -53,6 +53,9 @@
ConvertedModuleTypeCount map[string]uint64 `protobuf:"bytes,6,rep,name=convertedModuleTypeCount,proto3" json:"convertedModuleTypeCount,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"varint,2,opt,name=value,proto3"`
// Counts of total modules by module type.
TotalModuleTypeCount map[string]uint64 `protobuf:"bytes,7,rep,name=totalModuleTypeCount,proto3" json:"totalModuleTypeCount,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"varint,2,opt,name=value,proto3"`
+ // List of traced runtime events of bp2build, useful for tracking bp2build
+ // runtime.
+ Events []*Event `protobuf:"bytes,8,rep,name=events,proto3" json:"events,omitempty"`
}
func (x *Bp2BuildMetrics) Reset() {
@@ -136,13 +139,89 @@
return nil
}
+func (x *Bp2BuildMetrics) GetEvents() []*Event {
+ if x != nil {
+ return x.Events
+ }
+ return nil
+}
+
+// Traced runtime event of bp2build.
+type Event struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ // The event name.
+ Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
+ // The absolute start time of the event
+ // The number of nanoseconds elapsed since January 1, 1970 UTC.
+ StartTime uint64 `protobuf:"varint,2,opt,name=start_time,json=startTime,proto3" json:"start_time,omitempty"`
+ // The real running time.
+ // The number of nanoseconds elapsed since start_time.
+ RealTime uint64 `protobuf:"varint,3,opt,name=real_time,json=realTime,proto3" json:"real_time,omitempty"`
+}
+
+func (x *Event) Reset() {
+ *x = Event{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_bp2build_metrics_proto_msgTypes[1]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *Event) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Event) ProtoMessage() {}
+
+func (x *Event) ProtoReflect() protoreflect.Message {
+ mi := &file_bp2build_metrics_proto_msgTypes[1]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use Event.ProtoReflect.Descriptor instead.
+func (*Event) Descriptor() ([]byte, []int) {
+ return file_bp2build_metrics_proto_rawDescGZIP(), []int{1}
+}
+
+func (x *Event) GetName() string {
+ if x != nil {
+ return x.Name
+ }
+ return ""
+}
+
+func (x *Event) GetStartTime() uint64 {
+ if x != nil {
+ return x.StartTime
+ }
+ return 0
+}
+
+func (x *Event) GetRealTime() uint64 {
+ if x != nil {
+ return x.RealTime
+ }
+ return 0
+}
+
var File_bp2build_metrics_proto protoreflect.FileDescriptor
var file_bp2build_metrics_proto_rawDesc = []byte{
0x0a, 0x16, 0x62, 0x70, 0x32, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69,
0x63, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x1c, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f,
0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x62, 0x70, 0x32, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d,
- 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x22, 0xac, 0x06, 0x0a, 0x0f, 0x42, 0x70, 0x32, 0x42, 0x75,
+ 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x22, 0xe9, 0x06, 0x0a, 0x0f, 0x42, 0x70, 0x32, 0x42, 0x75,
0x69, 0x6c, 0x64, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x12, 0x32, 0x0a, 0x14, 0x67, 0x65,
0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x43, 0x6f, 0x75,
0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x14, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61,
@@ -179,24 +258,34 @@
0x42, 0x75, 0x69, 0x6c, 0x64, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x54, 0x6f, 0x74,
0x61, 0x6c, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x43, 0x6f, 0x75, 0x6e,
0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x14, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x4d, 0x6f, 0x64,
- 0x75, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x1a, 0x41, 0x0a, 0x13,
- 0x52, 0x75, 0x6c, 0x65, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x45, 0x6e,
- 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
- 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02,
- 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a,
- 0x4b, 0x0a, 0x1d, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x65, 0x64, 0x4d, 0x6f, 0x64, 0x75,
- 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79,
+ 0x75, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x3b, 0x0a, 0x06,
+ 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x73,
+ 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x62, 0x70, 0x32, 0x62, 0x75,
+ 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x45, 0x76, 0x65, 0x6e,
+ 0x74, 0x52, 0x06, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x1a, 0x41, 0x0a, 0x13, 0x52, 0x75, 0x6c,
+ 0x65, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79,
0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b,
0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28,
- 0x04, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x47, 0x0a, 0x19,
- 0x54, 0x6f, 0x74, 0x61, 0x6c, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x43,
- 0x6f, 0x75, 0x6e, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79,
- 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76,
- 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75,
- 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, 0x31, 0x5a, 0x2f, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64,
- 0x2f, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x2f, 0x75, 0x69, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63,
- 0x73, 0x2f, 0x62, 0x70, 0x32, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69,
- 0x63, 0x73, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+ 0x04, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x4b, 0x0a, 0x1d,
+ 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x65, 0x64, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x54,
+ 0x79, 0x70, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a,
+ 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12,
+ 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05,
+ 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x47, 0x0a, 0x19, 0x54, 0x6f, 0x74,
+ 0x61, 0x6c, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x43, 0x6f, 0x75, 0x6e,
+ 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20,
+ 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75,
+ 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02,
+ 0x38, 0x01, 0x22, 0x57, 0x0a, 0x05, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e,
+ 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12,
+ 0x1d, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x02, 0x20,
+ 0x01, 0x28, 0x04, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x1b,
+ 0x0a, 0x09, 0x72, 0x65, 0x61, 0x6c, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28,
+ 0x04, 0x52, 0x08, 0x72, 0x65, 0x61, 0x6c, 0x54, 0x69, 0x6d, 0x65, 0x42, 0x31, 0x5a, 0x2f, 0x61,
+ 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x2f, 0x75, 0x69, 0x2f,
+ 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2f, 0x62, 0x70, 0x32, 0x62, 0x75, 0x69, 0x6c, 0x64,
+ 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06,
+ 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
@@ -211,22 +300,24 @@
return file_bp2build_metrics_proto_rawDescData
}
-var file_bp2build_metrics_proto_msgTypes = make([]protoimpl.MessageInfo, 4)
+var file_bp2build_metrics_proto_msgTypes = make([]protoimpl.MessageInfo, 5)
var file_bp2build_metrics_proto_goTypes = []interface{}{
(*Bp2BuildMetrics)(nil), // 0: soong_build_bp2build_metrics.Bp2BuildMetrics
- nil, // 1: soong_build_bp2build_metrics.Bp2BuildMetrics.RuleClassCountEntry
- nil, // 2: soong_build_bp2build_metrics.Bp2BuildMetrics.ConvertedModuleTypeCountEntry
- nil, // 3: soong_build_bp2build_metrics.Bp2BuildMetrics.TotalModuleTypeCountEntry
+ (*Event)(nil), // 1: soong_build_bp2build_metrics.Event
+ nil, // 2: soong_build_bp2build_metrics.Bp2BuildMetrics.RuleClassCountEntry
+ nil, // 3: soong_build_bp2build_metrics.Bp2BuildMetrics.ConvertedModuleTypeCountEntry
+ nil, // 4: soong_build_bp2build_metrics.Bp2BuildMetrics.TotalModuleTypeCountEntry
}
var file_bp2build_metrics_proto_depIdxs = []int32{
- 1, // 0: soong_build_bp2build_metrics.Bp2BuildMetrics.ruleClassCount:type_name -> soong_build_bp2build_metrics.Bp2BuildMetrics.RuleClassCountEntry
- 2, // 1: soong_build_bp2build_metrics.Bp2BuildMetrics.convertedModuleTypeCount:type_name -> soong_build_bp2build_metrics.Bp2BuildMetrics.ConvertedModuleTypeCountEntry
- 3, // 2: soong_build_bp2build_metrics.Bp2BuildMetrics.totalModuleTypeCount:type_name -> soong_build_bp2build_metrics.Bp2BuildMetrics.TotalModuleTypeCountEntry
- 3, // [3:3] is the sub-list for method output_type
- 3, // [3:3] is the sub-list for method input_type
- 3, // [3:3] is the sub-list for extension type_name
- 3, // [3:3] is the sub-list for extension extendee
- 0, // [0:3] is the sub-list for field type_name
+ 2, // 0: soong_build_bp2build_metrics.Bp2BuildMetrics.ruleClassCount:type_name -> soong_build_bp2build_metrics.Bp2BuildMetrics.RuleClassCountEntry
+ 3, // 1: soong_build_bp2build_metrics.Bp2BuildMetrics.convertedModuleTypeCount:type_name -> soong_build_bp2build_metrics.Bp2BuildMetrics.ConvertedModuleTypeCountEntry
+ 4, // 2: soong_build_bp2build_metrics.Bp2BuildMetrics.totalModuleTypeCount:type_name -> soong_build_bp2build_metrics.Bp2BuildMetrics.TotalModuleTypeCountEntry
+ 1, // 3: soong_build_bp2build_metrics.Bp2BuildMetrics.events:type_name -> soong_build_bp2build_metrics.Event
+ 4, // [4:4] is the sub-list for method output_type
+ 4, // [4:4] is the sub-list for method input_type
+ 4, // [4:4] is the sub-list for extension type_name
+ 4, // [4:4] is the sub-list for extension extendee
+ 0, // [0:4] is the sub-list for field type_name
}
func init() { file_bp2build_metrics_proto_init() }
@@ -247,6 +338,18 @@
return nil
}
}
+ file_bp2build_metrics_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*Event); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
}
type x struct{}
out := protoimpl.TypeBuilder{
@@ -254,7 +357,7 @@
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_bp2build_metrics_proto_rawDesc,
NumEnums: 0,
- NumMessages: 4,
+ NumMessages: 5,
NumExtensions: 0,
NumServices: 0,
},
diff --git a/ui/metrics/bp2build_metrics_proto/bp2build_metrics.proto b/ui/metrics/bp2build_metrics_proto/bp2build_metrics.proto
index 6d98a3d..19a7827 100644
--- a/ui/metrics/bp2build_metrics_proto/bp2build_metrics.proto
+++ b/ui/metrics/bp2build_metrics_proto/bp2build_metrics.proto
@@ -38,4 +38,22 @@
// Counts of total modules by module type.
map<string, uint64> totalModuleTypeCount = 7;
+
+ // List of traced runtime events of bp2build, useful for tracking bp2build
+ // runtime.
+ repeated Event events = 8;
+}
+
+// Traced runtime event of bp2build.
+message Event {
+ // The event name.
+ string name = 1;
+
+ // The absolute start time of the event
+ // The number of nanoseconds elapsed since January 1, 1970 UTC.
+ uint64 start_time = 2;
+
+ // The real running time.
+ // The number of nanoseconds elapsed since start_time.
+ uint64 real_time = 3;
}
diff --git a/ui/metrics/metrics_proto/metrics.pb.go b/ui/metrics/metrics_proto/metrics.pb.go
index 2e530b0..26229c6 100644
--- a/ui/metrics/metrics_proto/metrics.pb.go
+++ b/ui/metrics/metrics_proto/metrics.pb.go
@@ -1072,6 +1072,8 @@
TotalAllocSize *uint64 `protobuf:"varint,4,opt,name=total_alloc_size,json=totalAllocSize" json:"total_alloc_size,omitempty"`
// The approximate maximum size of the heap in soong_build in bytes.
MaxHeapSize *uint64 `protobuf:"varint,5,opt,name=max_heap_size,json=maxHeapSize" json:"max_heap_size,omitempty"`
+ // Runtime metrics for soong_build execution.
+ Events []*PerfInfo `protobuf:"bytes,6,rep,name=events" json:"events,omitempty"`
}
func (x *SoongBuildMetrics) Reset() {
@@ -1141,6 +1143,13 @@
return 0
}
+func (x *SoongBuildMetrics) GetEvents() []*PerfInfo {
+ if x != nil {
+ return x.Events
+ }
+ return nil
+}
+
var File_metrics_proto protoreflect.FileDescriptor
var file_metrics_proto_rawDesc = []byte{
@@ -1340,7 +1349,7 @@
0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e,
0x43, 0x72, 0x69, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x55, 0x73, 0x65, 0x72, 0x4a, 0x6f, 0x75, 0x72,
0x6e, 0x65, 0x79, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, 0x04, 0x63, 0x75, 0x6a, 0x73,
- 0x22, 0xc3, 0x01, 0x0a, 0x11, 0x53, 0x6f, 0x6f, 0x6e, 0x67, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x4d,
+ 0x22, 0xfa, 0x01, 0x0a, 0x11, 0x53, 0x6f, 0x6f, 0x6e, 0x67, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x4d,
0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65,
0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73,
0x12, 0x1a, 0x0a, 0x08, 0x76, 0x61, 0x72, 0x69, 0x61, 0x6e, 0x74, 0x73, 0x18, 0x02, 0x20, 0x01,
@@ -1352,9 +1361,13 @@
0x28, 0x04, 0x52, 0x0e, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x41, 0x6c, 0x6c, 0x6f, 0x63, 0x53, 0x69,
0x7a, 0x65, 0x12, 0x22, 0x0a, 0x0d, 0x6d, 0x61, 0x78, 0x5f, 0x68, 0x65, 0x61, 0x70, 0x5f, 0x73,
0x69, 0x7a, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x6d, 0x61, 0x78, 0x48, 0x65,
- 0x61, 0x70, 0x53, 0x69, 0x7a, 0x65, 0x42, 0x28, 0x5a, 0x26, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69,
- 0x64, 0x2f, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x2f, 0x75, 0x69, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69,
- 0x63, 0x73, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f,
+ 0x61, 0x70, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x35, 0x0a, 0x06, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73,
+ 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62,
+ 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x50, 0x65, 0x72,
+ 0x66, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x06, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x42, 0x28, 0x5a,
+ 0x26, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x2f, 0x75,
+ 0x69, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63,
+ 0x73, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f,
}
var (
@@ -1403,11 +1416,12 @@
2, // 14: soong_build_metrics.ModuleTypeInfo.build_system:type_name -> soong_build_metrics.ModuleTypeInfo.BuildSystem
3, // 15: soong_build_metrics.CriticalUserJourneyMetrics.metrics:type_name -> soong_build_metrics.MetricsBase
9, // 16: soong_build_metrics.CriticalUserJourneysMetrics.cujs:type_name -> soong_build_metrics.CriticalUserJourneyMetrics
- 17, // [17:17] is the sub-list for method output_type
- 17, // [17:17] is the sub-list for method input_type
- 17, // [17:17] is the sub-list for extension type_name
- 17, // [17:17] is the sub-list for extension extendee
- 0, // [0:17] is the sub-list for field type_name
+ 6, // 17: soong_build_metrics.SoongBuildMetrics.events:type_name -> soong_build_metrics.PerfInfo
+ 18, // [18:18] is the sub-list for method output_type
+ 18, // [18:18] is the sub-list for method input_type
+ 18, // [18:18] is the sub-list for extension type_name
+ 18, // [18:18] is the sub-list for extension extendee
+ 0, // [0:18] is the sub-list for field type_name
}
func init() { file_metrics_proto_init() }
diff --git a/ui/metrics/metrics_proto/metrics.proto b/ui/metrics/metrics_proto/metrics.proto
index db0a14a..26e4d73 100644
--- a/ui/metrics/metrics_proto/metrics.proto
+++ b/ui/metrics/metrics_proto/metrics.proto
@@ -235,4 +235,7 @@
// The approximate maximum size of the heap in soong_build in bytes.
optional uint64 max_heap_size = 5;
+
+ // Runtime metrics for soong_build execution.
+ repeated PerfInfo events = 6;
}