diff options
40 files changed, 759 insertions, 461 deletions
diff --git a/aconfig/codegen/java_aconfig_library.go b/aconfig/codegen/java_aconfig_library.go index ebca4134c..9f399bf76 100644 --- a/aconfig/codegen/java_aconfig_library.go +++ b/aconfig/codegen/java_aconfig_library.go @@ -72,7 +72,7 @@ func (callbacks *JavaAconfigDeclarationsLibraryCallbacks) DepsMutator(module *ja module.AddSharedLibrary("aconfig-annotations-lib") // TODO(b/303773055): Remove the annotation after access issue is resolved. module.AddSharedLibrary("unsupportedappusage") - module.AddSharedLibrary("aconfig_storage_reader_java") + module.AddSharedLibrary("aconfig_storage_stub") } } diff --git a/android/Android.bp b/android/Android.bp index a9a3564ab..dfea8f999 100644 --- a/android/Android.bp +++ b/android/Android.bp @@ -111,6 +111,7 @@ bootstrap_go_package { "testing.go", "util.go", "variable.go", + "vendor_api_levels.go", "vintf_fragment.go", "vintf_data.go", "visibility.go", diff --git a/android/apex.go b/android/apex.go index 414d4e140..db9391204 100644 --- a/android/apex.go +++ b/android/apex.go @@ -16,7 +16,6 @@ package android import ( "fmt" - "reflect" "slices" "sort" "strconv" @@ -62,14 +61,6 @@ type ApexInfo struct { // that are merged together. InApexVariants []string - // List of APEX Soong module names that this module is part of. Note that the list includes - // different variations of the same APEX. For example, if module `foo` is included in the - // apex `com.android.foo`, and also if there is an override_apex module - // `com.mycompany.android.foo` overriding `com.android.foo`, then this list contains both - // `com.android.foo` and `com.mycompany.android.foo`. If the APEX Soong module is a - // prebuilt, the name here doesn't have the `prebuilt_` prefix. - InApexModules []string - // True if this is for a prebuilt_apex. // // If true then this will customize the apex processing to make it suitable for handling @@ -100,7 +91,6 @@ func (i ApexInfo) AddJSONData(d *map[string]interface{}) { (*d)["Apex"] = map[string]interface{}{ "ApexVariationName": i.ApexVariationName, "MinSdkVersion": i.MinSdkVersion, - "InApexModules": i.InApexModules, "InApexVariants": i.InApexVariants, "ForPrebuiltApex": i.ForPrebuiltApex, } @@ -135,15 +125,6 @@ func (i ApexInfo) InApexVariant(apexVariant string) bool { return false } -func (i ApexInfo) InApexModule(apexModuleName string) bool { - for _, a := range i.InApexModules { - if a == apexModuleName { - return true - } - } - return false -} - // To satisfy the comparable interface func (i ApexInfo) Equal(other any) bool { otherApexInfo, ok := other.(ApexInfo) @@ -151,8 +132,7 @@ func (i ApexInfo) Equal(other any) bool { i.MinSdkVersion == otherApexInfo.MinSdkVersion && i.Updatable == otherApexInfo.Updatable && i.UsePlatformApis == otherApexInfo.UsePlatformApis && - reflect.DeepEqual(i.InApexVariants, otherApexInfo.InApexVariants) && - reflect.DeepEqual(i.InApexModules, otherApexInfo.InApexModules) + slices.Equal(i.InApexVariants, otherApexInfo.InApexVariants) } // ApexBundleInfo contains information about the dependencies of an apex @@ -272,9 +252,6 @@ type ApexProperties struct { // Default is ["//apex_available:platform"]. Apex_available []string - // See ApexModule.InAnyApex() - InAnyApex bool `blueprint:"mutated"` - // See ApexModule.NotAvailableForPlatform() NotAvailableForPlatform bool `blueprint:"mutated"` @@ -361,30 +338,22 @@ func (m *ApexModuleBase) ApexAvailable() []string { func (m *ApexModuleBase) BuildForApex(apex ApexInfo) { m.apexInfosLock.Lock() defer m.apexInfosLock.Unlock() - for i, v := range m.apexInfos { - if v.ApexVariationName == apex.ApexVariationName { - if len(apex.InApexModules) != 1 { - panic(fmt.Errorf("Newly created apexInfo must be for a single APEX")) - } - // Even when the ApexVariantNames are the same, the given ApexInfo might - // actually be for different APEX. This can happen when an APEX is - // overridden via override_apex. For example, there can be two apexes - // `com.android.foo` (from the `apex` module type) and - // `com.mycompany.android.foo` (from the `override_apex` module type), both - // of which has the same ApexVariantName `com.android.foo`. Add the apex - // name to the list so that it's not lost. - if !InList(apex.InApexModules[0], v.InApexModules) { - m.apexInfos[i].InApexModules = append(m.apexInfos[i].InApexModules, apex.InApexModules[0]) - } - return - } + if slices.ContainsFunc(m.apexInfos, func(existing ApexInfo) bool { + return existing.ApexVariationName == apex.ApexVariationName + }) { + return } m.apexInfos = append(m.apexInfos, apex) } // Implements ApexModule func (m *ApexModuleBase) InAnyApex() bool { - return m.ApexProperties.InAnyApex + for _, apex_name := range m.ApexProperties.Apex_available { + if apex_name != AvailableToPlatform { + return true + } + } + return false } // Implements ApexModule @@ -546,7 +515,6 @@ func mergeApexVariations(apexInfos []ApexInfo) (merged []ApexInfo, aliases [][2] if index, exists := seen[mergedName]; exists { // Variants having the same mergedName are deduped merged[index].InApexVariants = append(merged[index].InApexVariants, variantName) - merged[index].InApexModules = append(merged[index].InApexModules, apexInfo.InApexModules...) merged[index].Updatable = merged[index].Updatable || apexInfo.Updatable // Platform APIs is allowed for this module only when all APEXes containing // the module are with `use_platform_apis: true`. @@ -556,7 +524,6 @@ func mergeApexVariations(apexInfos []ApexInfo) (merged []ApexInfo, aliases [][2] seen[mergedName] = len(merged) apexInfo.ApexVariationName = mergedName apexInfo.InApexVariants = CopyOf(apexInfo.InApexVariants) - apexInfo.InApexModules = CopyOf(apexInfo.InApexModules) apexInfo.TestApexes = CopyOf(apexInfo.TestApexes) merged = append(merged, apexInfo) } @@ -644,8 +611,6 @@ func MutateApexTransition(ctx BaseModuleContext, variation string) { apexInfos, _ = mergeApexVariations(apexInfos) } - base.ApexProperties.InAnyApex = true - if platformVariation && !ctx.Host() && !module.AvailableFor(AvailableToPlatform) && module.NotAvailableForPlatform() { // Do not install the module for platform, but still allow it to output // uninstallable AndroidMk entries in certain cases when they have side diff --git a/android/apex_test.go b/android/apex_test.go index 347bf7d98..78597b234 100644 --- a/android/apex_test.go +++ b/android/apex_test.go @@ -37,7 +37,6 @@ func Test_mergeApexVariations(t *testing.T) { ApexVariationName: "foo", MinSdkVersion: FutureApiLevel, InApexVariants: []string{"foo"}, - InApexModules: []string{"foo"}, ForPrebuiltApex: NotForPrebuiltApex, }, }, @@ -46,7 +45,6 @@ func Test_mergeApexVariations(t *testing.T) { ApexVariationName: "apex10000", MinSdkVersion: FutureApiLevel, InApexVariants: []string{"foo"}, - InApexModules: []string{"foo"}, ForPrebuiltApex: NotForPrebuiltApex, }, }, @@ -61,14 +59,12 @@ func Test_mergeApexVariations(t *testing.T) { ApexVariationName: "foo", MinSdkVersion: FutureApiLevel, InApexVariants: []string{"foo"}, - InApexModules: []string{"foo"}, ForPrebuiltApex: NotForPrebuiltApex, }, { ApexVariationName: "bar", MinSdkVersion: FutureApiLevel, InApexVariants: []string{"bar"}, - InApexModules: []string{"bar"}, ForPrebuiltApex: NotForPrebuiltApex, }, }, @@ -77,7 +73,6 @@ func Test_mergeApexVariations(t *testing.T) { ApexVariationName: "apex10000", MinSdkVersion: FutureApiLevel, InApexVariants: []string{"foo", "bar"}, - InApexModules: []string{"foo", "bar"}, }}, wantAliases: [][2]string{ {"foo", "apex10000"}, @@ -91,14 +86,12 @@ func Test_mergeApexVariations(t *testing.T) { ApexVariationName: "foo", MinSdkVersion: FutureApiLevel, InApexVariants: []string{"foo"}, - InApexModules: []string{"foo"}, ForPrebuiltApex: NotForPrebuiltApex, }, { ApexVariationName: "bar", MinSdkVersion: uncheckedFinalApiLevel(30), InApexVariants: []string{"bar"}, - InApexModules: []string{"bar"}, ForPrebuiltApex: NotForPrebuiltApex, }, }, @@ -107,14 +100,12 @@ func Test_mergeApexVariations(t *testing.T) { ApexVariationName: "apex10000", MinSdkVersion: FutureApiLevel, InApexVariants: []string{"foo"}, - InApexModules: []string{"foo"}, ForPrebuiltApex: NotForPrebuiltApex, }, { ApexVariationName: "apex30", MinSdkVersion: uncheckedFinalApiLevel(30), InApexVariants: []string{"bar"}, - InApexModules: []string{"bar"}, ForPrebuiltApex: NotForPrebuiltApex, }, }, @@ -130,7 +121,6 @@ func Test_mergeApexVariations(t *testing.T) { ApexVariationName: "foo", MinSdkVersion: FutureApiLevel, InApexVariants: []string{"foo"}, - InApexModules: []string{"foo"}, ForPrebuiltApex: NotForPrebuiltApex, }, { @@ -138,7 +128,6 @@ func Test_mergeApexVariations(t *testing.T) { MinSdkVersion: FutureApiLevel, Updatable: true, InApexVariants: []string{"bar"}, - InApexModules: []string{"bar"}, ForPrebuiltApex: NotForPrebuiltApex, }, }, @@ -148,7 +137,6 @@ func Test_mergeApexVariations(t *testing.T) { MinSdkVersion: FutureApiLevel, Updatable: true, InApexVariants: []string{"foo", "bar"}, - InApexModules: []string{"foo", "bar"}, ForPrebuiltApex: NotForPrebuiltApex, }, }, @@ -164,7 +152,6 @@ func Test_mergeApexVariations(t *testing.T) { ApexVariationName: "foo", MinSdkVersion: FutureApiLevel, InApexVariants: []string{"foo"}, - InApexModules: []string{"foo"}, ForPrebuiltApex: NotForPrebuiltApex, }, { @@ -172,7 +159,6 @@ func Test_mergeApexVariations(t *testing.T) { MinSdkVersion: FutureApiLevel, Updatable: true, InApexVariants: []string{"bar"}, - InApexModules: []string{"bar"}, ForPrebuiltApex: NotForPrebuiltApex, }, // This one should not be merged in with the others because it is for @@ -182,7 +168,6 @@ func Test_mergeApexVariations(t *testing.T) { MinSdkVersion: FutureApiLevel, Updatable: true, InApexVariants: []string{"baz"}, - InApexModules: []string{"baz"}, ForPrebuiltApex: ForPrebuiltApex, }, }, @@ -192,7 +177,6 @@ func Test_mergeApexVariations(t *testing.T) { MinSdkVersion: FutureApiLevel, Updatable: true, InApexVariants: []string{"foo", "bar"}, - InApexModules: []string{"foo", "bar"}, ForPrebuiltApex: NotForPrebuiltApex, }, { @@ -200,7 +184,6 @@ func Test_mergeApexVariations(t *testing.T) { MinSdkVersion: FutureApiLevel, Updatable: true, InApexVariants: []string{"baz"}, - InApexModules: []string{"baz"}, ForPrebuiltApex: ForPrebuiltApex, }, }, @@ -216,7 +199,6 @@ func Test_mergeApexVariations(t *testing.T) { ApexVariationName: "foo", MinSdkVersion: FutureApiLevel, InApexVariants: []string{"foo"}, - InApexModules: []string{"foo"}, ForPrebuiltApex: NotForPrebuiltApex, }, { @@ -224,7 +206,6 @@ func Test_mergeApexVariations(t *testing.T) { MinSdkVersion: FutureApiLevel, UsePlatformApis: true, InApexVariants: []string{"bar"}, - InApexModules: []string{"bar"}, ForPrebuiltApex: NotForPrebuiltApex, }, }, @@ -233,7 +214,6 @@ func Test_mergeApexVariations(t *testing.T) { ApexVariationName: "apex10000", MinSdkVersion: FutureApiLevel, InApexVariants: []string{"foo", "bar"}, - InApexModules: []string{"foo", "bar"}, ForPrebuiltApex: NotForPrebuiltApex, }, }, @@ -250,7 +230,6 @@ func Test_mergeApexVariations(t *testing.T) { MinSdkVersion: FutureApiLevel, UsePlatformApis: true, InApexVariants: []string{"foo"}, - InApexModules: []string{"foo"}, ForPrebuiltApex: NotForPrebuiltApex, }, { @@ -258,7 +237,6 @@ func Test_mergeApexVariations(t *testing.T) { MinSdkVersion: FutureApiLevel, UsePlatformApis: true, InApexVariants: []string{"bar"}, - InApexModules: []string{"bar"}, ForPrebuiltApex: NotForPrebuiltApex, }, }, @@ -268,7 +246,6 @@ func Test_mergeApexVariations(t *testing.T) { MinSdkVersion: FutureApiLevel, UsePlatformApis: true, InApexVariants: []string{"foo", "bar"}, - InApexModules: []string{"foo", "bar"}, ForPrebuiltApex: NotForPrebuiltApex, }, }, diff --git a/android/container.go b/android/container.go index 2a3777b30..27b17ed99 100644 --- a/android/container.go +++ b/android/container.go @@ -93,7 +93,7 @@ var globallyAllowlistedDependencies = []string{ // TODO(b/363016634): Remove from the allowlist when the module is converted // to java_sdk_library and the java_aconfig_library modules depend on the stub. - "aconfig_storage_reader_java", + "aconfig_storage_stub", // framework-res provides core resources essential for building apps and system UI. // This module is implicitly added as a dependency for java modules even when the @@ -382,7 +382,7 @@ func (c *ContainersInfo) BelongingContainers() []*container { func (c *ContainersInfo) ApexNames() (ret []string) { for _, apex := range c.belongingApexes { - ret = append(ret, apex.InApexModules...) + ret = append(ret, apex.InApexVariants...) } slices.Sort(ret) return ret diff --git a/android/module.go b/android/module.go index ce995ad77..5437d48ee 100644 --- a/android/module.go +++ b/android/module.go @@ -117,6 +117,7 @@ type Module interface { HostRequiredModuleNames() []string TargetRequiredModuleNames() []string VintfFragmentModuleNames(ctx ConfigurableEvaluatorContext) []string + VintfFragments(ctx ConfigurableEvaluatorContext) []string ConfigurableEvaluator(ctx ConfigurableEvaluatorContext) proptools.ConfigurableEvaluator @@ -1626,6 +1627,10 @@ func (m *ModuleBase) VintfFragmentModuleNames(ctx ConfigurableEvaluatorContext) return m.base().commonProperties.Vintf_fragment_modules.GetOrDefault(m.ConfigurableEvaluator(ctx), nil) } +func (m *ModuleBase) VintfFragments(ctx ConfigurableEvaluatorContext) []string { + return m.base().commonProperties.Vintf_fragments.GetOrDefault(m.ConfigurableEvaluator(ctx), nil) +} + func (m *ModuleBase) generateVariantTarget(ctx *moduleContext) { namespacePrefix := ctx.Namespace().id if namespacePrefix != "" { @@ -2450,6 +2455,8 @@ func (e configurationEvalutor) EvaluateConfiguration(condition proptools.Configu return proptools.ConfigurableValueString(v) case "bool": return proptools.ConfigurableValueBool(v == "true") + case "string_list": + return proptools.ConfigurableValueStringList(strings.Split(v, " ")) default: panic("unhandled soong config variable type: " + ty) } diff --git a/android/module_proxy.go b/android/module_proxy.go index 1f9679926..30459b9cd 100644 --- a/android/module_proxy.go +++ b/android/module_proxy.go @@ -9,6 +9,8 @@ type ModuleProxy struct { module blueprint.ModuleProxy } +var _ Module = (*ModuleProxy)(nil) + func (m ModuleProxy) Name() string { return m.module.Name() } @@ -225,3 +227,7 @@ func (m ModuleProxy) DecodeMultilib(ctx ConfigContext) (string, string) { func (m ModuleProxy) Overrides() []string { panic("method is not implemented on ModuleProxy") } + +func (m ModuleProxy) VintfFragments(ctx ConfigurableEvaluatorContext) []string { + panic("method is not implemented on ModuleProxy") +} diff --git a/android/rule_builder.go b/android/rule_builder.go index a1573868b..83f8b9992 100644 --- a/android/rule_builder.go +++ b/android/rule_builder.go @@ -488,21 +488,15 @@ func (r *RuleBuilder) depFileMergerCmd(depFiles WritablePaths) *RuleBuilderComma Inputs(depFiles.Paths()) } -// BuildWithNinjaVars adds the built command line to the build graph, with dependencies on Inputs and Tools, and output files for -// Outputs. This function will not escape Ninja variables, so it may be used to write sandbox manifests using Ninja variables. -func (r *RuleBuilder) BuildWithUnescapedNinjaVars(name string, desc string) { - r.build(name, desc, false) -} - // Build adds the built command line to the build graph, with dependencies on Inputs and Tools, and output files for // Outputs. func (r *RuleBuilder) Build(name string, desc string) { - r.build(name, desc, true) + r.build(name, desc) } var sandboxEnvOnceKey = NewOnceKey("sandbox_environment_variables") -func (r *RuleBuilder) build(name string, desc string, ninjaEscapeCommandString bool) { +func (r *RuleBuilder) build(name string, desc string) { name = ninjaNameEscape(name) if len(r.missingDeps) > 0 { @@ -765,30 +759,7 @@ func (r *RuleBuilder) build(name string, desc string, ninjaEscapeCommandString b if err != nil { ReportPathErrorf(r.ctx, "sbox manifest failed to marshal: %q", err) } - if ninjaEscapeCommandString { - WriteFileRule(r.ctx, r.sboxManifestPath, string(pbText)) - } else { - // We need to have a rule to write files that is - // defined on the RuleBuilder's pctx in order to - // write Ninja variables in the string. - // The WriteFileRule function above rule can only write - // raw strings because it is defined on the android - // package's pctx, and it can't access variables defined - // in another context. - r.ctx.Build(r.pctx, BuildParams{ - Rule: r.ctx.Rule(r.pctx, "unescapedWriteFile", blueprint.RuleParams{ - Command: `rm -rf ${out} && cat ${out}.rsp > ${out}`, - Rspfile: "${out}.rsp", - RspfileContent: "${content}", - Description: "write file", - }, "content"), - Output: r.sboxManifestPath, - Description: "write sbox manifest " + r.sboxManifestPath.Base(), - Args: map[string]string{ - "content": string(pbText), - }, - }) - } + WriteFileRule(r.ctx, r.sboxManifestPath, string(pbText)) // Generate a new string to use as the command line of the sbox rule. This uses // a RuleBuilderCommand as a convenience method of building the command line, then @@ -882,9 +853,7 @@ func (r *RuleBuilder) build(name string, desc string, ninjaEscapeCommandString b pool = localPool } - if ninjaEscapeCommandString { - commandString = proptools.NinjaEscape(commandString) - } + commandString = proptools.NinjaEscape(commandString) args_vars := make([]string, len(r.args)) i := 0 diff --git a/android/rule_builder_test.go b/android/rule_builder_test.go index 6a8a964a1..e1a1e08c4 100644 --- a/android/rule_builder_test.go +++ b/android/rule_builder_test.go @@ -475,10 +475,9 @@ type testRuleBuilderModule struct { Srcs []string Flags []string - Restat bool - Sbox bool - Sbox_inputs bool - Unescape_ninja_vars bool + Restat bool + Sbox bool + Sbox_inputs bool } } @@ -498,7 +497,7 @@ func (t *testRuleBuilderModule) GenerateAndroidBuildActions(ctx ModuleContext) { testRuleBuilder_Build(ctx, in, implicit, orderOnly, validation, t.properties.Flags, out, outDep, outDir, - manifestPath, t.properties.Restat, t.properties.Sbox, t.properties.Sbox_inputs, t.properties.Unescape_ninja_vars, + manifestPath, t.properties.Restat, t.properties.Sbox, t.properties.Sbox_inputs, rspFile, rspFileContents, rspFile2, rspFileContents2) } @@ -523,14 +522,14 @@ func (t *testRuleBuilderSingleton) GenerateBuildActions(ctx SingletonContext) { manifestPath := PathForOutput(ctx, "singleton/sbox.textproto") testRuleBuilder_Build(ctx, in, implicit, orderOnly, validation, nil, out, outDep, outDir, - manifestPath, true, false, false, false, + manifestPath, true, false, false, rspFile, rspFileContents, rspFile2, rspFileContents2) } func testRuleBuilder_Build(ctx BuilderContext, in Paths, implicit, orderOnly, validation Path, flags []string, out, outDep, outDir, manifestPath WritablePath, - restat, sbox, sboxInputs, unescapeNinjaVars bool, + restat, sbox, sboxInputs bool, rspFile WritablePath, rspFileContents Paths, rspFile2 WritablePath, rspFileContents2 Paths) { rule := NewRuleBuilder(pctx_ruleBuilderTest, ctx) @@ -558,11 +557,7 @@ func testRuleBuilder_Build(ctx BuilderContext, in Paths, implicit, orderOnly, va rule.Restat() } - if unescapeNinjaVars { - rule.BuildWithUnescapedNinjaVars("rule", "desc") - } else { - rule.Build("rule", "desc") - } + rule.Build("rule", "desc") } var prepareForRuleBuilderTest = FixtureRegisterWithContext(func(ctx RegistrationContext) { @@ -777,48 +772,3 @@ func TestRuleBuilderHashInputs(t *testing.T) { }) } } - -func TestRuleBuilderWithNinjaVarEscaping(t *testing.T) { - bp := ` - rule_builder_test { - name: "foo_sbox_escaped", - flags: ["${cmdFlags}"], - sbox: true, - sbox_inputs: true, - } - rule_builder_test { - name: "foo_sbox_unescaped", - flags: ["${cmdFlags}"], - sbox: true, - sbox_inputs: true, - unescape_ninja_vars: true, - } - ` - result := GroupFixturePreparers( - prepareForRuleBuilderTest, - FixtureWithRootAndroidBp(bp), - ).RunTest(t) - - escapedNinjaMod := result.ModuleForTests("foo_sbox_escaped", "").Output("sbox.textproto") - AssertStringEquals(t, "expected rule", "android/soong/android.rawFileCopy", escapedNinjaMod.Rule.String()) - AssertStringDoesContain( - t, - "", - ContentFromFileRuleForTests(t, result.TestContext, escapedNinjaMod), - "${cmdFlags}", - ) - - unescapedNinjaMod := result.ModuleForTests("foo_sbox_unescaped", "").Rule("unescapedWriteFile") - AssertStringDoesContain( - t, - "", - unescapedNinjaMod.BuildParams.Args["content"], - "${cmdFlags}", - ) - AssertStringDoesNotContain( - t, - "", - unescapedNinjaMod.BuildParams.Args["content"], - "$${cmdFlags}", - ) -} diff --git a/android/selects_test.go b/android/selects_test.go index 90d7091e0..1397ed8b7 100644 --- a/android/selects_test.go +++ b/android/selects_test.go @@ -1031,6 +1031,54 @@ my_module_type { my_string_list: &[]string{"d2", "e2", "f2", "a1", "b1", "c1"}, }, }, + { + name: "string list variables", + bp: ` +my_module_type { + name: "foo", + my_string_list: ["a"] + select(soong_config_variable("my_namespace", "my_var"), { + any @ my_var: my_var, + default: [], + }), +} +`, + vendorVars: map[string]map[string]string{ + "my_namespace": { + "my_var": "b c", + }, + }, + vendorVarTypes: map[string]map[string]string{ + "my_namespace": { + "my_var": "string_list", + }, + }, + provider: selectsTestProvider{ + my_string_list: &[]string{"a", "b", "c"}, + }, + }, + { + name: "string list variables don't match string matchers", + bp: ` +my_module_type { + name: "foo", + my_string_list: ["a"] + select(soong_config_variable("my_namespace", "my_var"), { + "foo": ["b"], + default: [], + }), +} +`, + vendorVars: map[string]map[string]string{ + "my_namespace": { + "my_var": "b c", + }, + }, + vendorVarTypes: map[string]map[string]string{ + "my_namespace": { + "my_var": "string_list", + }, + }, + expectedError: `Expected all branches of a select on condition soong_config_variable\("my_namespace", "my_var"\) to have type string_list, found string`, + }, } for _, tc := range testCases { diff --git a/android/test_config.go b/android/test_config.go index f2510387f..3609e6b78 100644 --- a/android/test_config.go +++ b/android/test_config.go @@ -45,6 +45,7 @@ func TestConfig(buildDir string, env map[string]string, bp string, fs map[string Platform_version_active_codenames: []string{"S", "Tiramisu"}, DeviceSystemSdkVersions: []string{"29", "30", "S"}, Platform_systemsdk_versions: []string{"29", "30", "S", "Tiramisu"}, + VendorApiLevel: stringPtr("202404"), AAPTConfig: []string{"normal", "large", "xlarge", "hdpi", "xhdpi", "xxhdpi"}, AAPTPreferredConfig: stringPtr("xhdpi"), AAPTCharacteristics: stringPtr("nosdcard"), diff --git a/android/variable.go b/android/variable.go index 7ae9a4382..36ddc1c81 100644 --- a/android/variable.go +++ b/android/variable.go @@ -610,19 +610,25 @@ type PartitionVariables struct { ProductUseDynamicPartitionSize bool `json:",omitempty"` CopyImagesForTargetFilesZip bool `json:",omitempty"` + VendorSecurityPatch string `json:",omitempty"` + // Boot image stuff - BuildingRamdiskImage bool `json:",omitempty"` - ProductBuildBootImage bool `json:",omitempty"` - ProductBuildVendorBootImage string `json:",omitempty"` - ProductBuildInitBootImage bool `json:",omitempty"` - BoardUsesRecoveryAsBoot bool `json:",omitempty"` - BoardPrebuiltBootimage string `json:",omitempty"` - BoardPrebuiltInitBootimage string `json:",omitempty"` - BoardBootimagePartitionSize string `json:",omitempty"` - BoardInitBootimagePartitionSize string `json:",omitempty"` - BoardBootHeaderVersion string `json:",omitempty"` - TargetKernelPath string `json:",omitempty"` - BoardUsesGenericKernelImage bool `json:",omitempty"` + BuildingRamdiskImage bool `json:",omitempty"` + ProductBuildBootImage bool `json:",omitempty"` + ProductBuildVendorBootImage string `json:",omitempty"` + ProductBuildInitBootImage bool `json:",omitempty"` + BoardUsesRecoveryAsBoot bool `json:",omitempty"` + BoardPrebuiltBootimage string `json:",omitempty"` + BoardPrebuiltInitBootimage string `json:",omitempty"` + BoardBootimagePartitionSize string `json:",omitempty"` + BoardInitBootimagePartitionSize string `json:",omitempty"` + BoardBootHeaderVersion string `json:",omitempty"` + TargetKernelPath string `json:",omitempty"` + BoardUsesGenericKernelImage bool `json:",omitempty"` + BootSecurityPatch string `json:",omitempty"` + InitBootSecurityPatch string `json:",omitempty"` + BoardIncludeDtbInBootimg bool `json:",omitempty"` + InternalKernelCmdline []string `json:",omitempty"` // Avb (android verified boot) stuff BoardAvbEnable bool `json:",omitempty"` diff --git a/android/vendor_api_levels.go b/android/vendor_api_levels.go new file mode 100644 index 000000000..4d364fde6 --- /dev/null +++ b/android/vendor_api_levels.go @@ -0,0 +1,49 @@ +// Copyright 2024 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package android + +import ( + "fmt" + "strconv" +) + +func getSdkVersionOfVendorApiLevel(apiLevel int) (int, bool) { + ok := true + sdkVersion := -1 + switch apiLevel { + case 202404: + sdkVersion = 35 + case 202504: + sdkVersion = 36 + default: + ok = false + } + return sdkVersion, ok +} + +func GetSdkVersionForVendorApiLevel(vendorApiLevel string) (ApiLevel, error) { + vendorApiLevelInt, err := strconv.Atoi(vendorApiLevel) + if err != nil { + return NoneApiLevel, fmt.Errorf("The vendor API level %q must be able to be parsed as an integer", vendorApiLevel) + } + if vendorApiLevelInt < 35 { + return uncheckedFinalApiLevel(vendorApiLevelInt), nil + } + + if sdkInt, ok := getSdkVersionOfVendorApiLevel(vendorApiLevelInt); ok { + return uncheckedFinalApiLevel(sdkInt), nil + } + return NoneApiLevel, fmt.Errorf("Unknown vendor API level %q. Requires updating the map in vendor_api_level.go?", vendorApiLevel) +} diff --git a/apex/apex.go b/apex/apex.go index ed9e58a63..0e40d7c0e 100644 --- a/apex/apex.go +++ b/apex/apex.go @@ -434,6 +434,7 @@ type apexBundle struct { archProperties apexArchBundleProperties overridableProperties overridableProperties vndkProperties apexVndkProperties // only for apex_vndk modules + testProperties apexTestProperties // only for apex_test modules /////////////////////////////////////////////////////////////////////////////////////////// // Inputs @@ -1020,7 +1021,6 @@ func (a *apexBundle) ApexInfoMutator(mctx android.TopDownMutatorContext) { Updatable: a.Updatable(), UsePlatformApis: a.UsePlatformApis(), InApexVariants: []string{apexVariationName}, - InApexModules: []string{a.Name()}, // could be com.mycompany.android.foo TestApexes: testApexes, BaseApexName: mctx.ModuleName(), ApexAvailableName: proptools.String(a.properties.Apex_available_name), @@ -1297,6 +1297,23 @@ func (a *apexBundle) UsePlatformApis() bool { return proptools.BoolDefault(a.properties.Platform_apis, false) } +type apexValidationType int + +const ( + hostApexVerifier apexValidationType = iota + apexSepolicyTests +) + +func (a *apexBundle) skipValidation(validationType apexValidationType) bool { + switch validationType { + case hostApexVerifier: + return proptools.Bool(a.testProperties.Skip_validations.Host_apex_verifier) + case apexSepolicyTests: + return proptools.Bool(a.testProperties.Skip_validations.Apex_sepolicy_tests) + } + panic("Unknown validation type") +} + // getCertString returns the name of the cert that should be used to sign this APEX. This is // basically from the "certificate" property, but could be overridden by the device config. func (a *apexBundle) getCertString(ctx android.BaseModuleContext) string { @@ -2428,10 +2445,14 @@ func newApexBundle() *apexBundle { return module } -func ApexBundleFactory(testApex bool) android.Module { - bundle := newApexBundle() - bundle.testApex = testApex - return bundle +type apexTestProperties struct { + // Boolean flags for validation checks. Test APEXes can turn on/off individual checks. + Skip_validations struct { + // Skips `Apex_sepolicy_tests` check if true + Apex_sepolicy_tests *bool + // Skips `Host_apex_verifier` check if true + Host_apex_verifier *bool + } } // apex_test is an APEX for testing. The difference from the ordinary apex module type is that @@ -2439,6 +2460,7 @@ func ApexBundleFactory(testApex bool) android.Module { func TestApexBundleFactory() android.Module { bundle := newApexBundle() bundle.testApex = true + bundle.AddProperties(&bundle.testProperties) return bundle } diff --git a/apex/apex_test.go b/apex/apex_test.go index 5b5fe5ff1..6e9295911 100644 --- a/apex/apex_test.go +++ b/apex/apex_test.go @@ -7101,6 +7101,51 @@ func TestApexAvailable_PrefixMatch(t *testing.T) { `) } +func TestApexValidation_TestApexCanSkipInitRcCheck(t *testing.T) { + t.Parallel() + ctx := testApex(t, ` + apex_test { + name: "myapex", + key: "myapex.key", + skip_validations: { + host_apex_verifier: true, + }, + updatable: false, + } + apex_key { + name: "myapex.key", + public_key: "testkey.avbpubkey", + private_key: "testkey.pem", + } + `) + + validations := ctx.ModuleForTests("myapex", "android_common_myapex").Rule("signapk").Validations.Strings() + if android.SuffixInList(validations, "host_apex_verifier.timestamp") { + t.Error("should not run host_apex_verifier") + } +} + +func TestApexValidation_TestApexCheckInitRc(t *testing.T) { + t.Parallel() + ctx := testApex(t, ` + apex_test { + name: "myapex", + key: "myapex.key", + updatable: false, + } + apex_key { + name: "myapex.key", + public_key: "testkey.avbpubkey", + private_key: "testkey.pem", + } + `) + + validations := ctx.ModuleForTests("myapex", "android_common_myapex").Rule("signapk").Validations.Strings() + if !android.SuffixInList(validations, "host_apex_verifier.timestamp") { + t.Error("should run host_apex_verifier") + } +} + func TestOverrideApex(t *testing.T) { t.Parallel() ctx := testApex(t, ` diff --git a/apex/builder.go b/apex/builder.go index d0acc8d6c..e5ae10622 100644 --- a/apex/builder.go +++ b/apex/builder.go @@ -924,14 +924,14 @@ func (a *apexBundle) buildApex(ctx android.ModuleContext) { var validations android.Paths validations = append(validations, runApexLinkerconfigValidation(ctx, unsignedOutputFile, imageDir)) // TODO(b/279688635) deapexer supports [ext4] - if !a.testApex && suffix == imageApexSuffix && ext4 == a.payloadFsType { + if !a.skipValidation(apexSepolicyTests) && suffix == imageApexSuffix && ext4 == a.payloadFsType { validations = append(validations, runApexSepolicyTests(ctx, unsignedOutputFile)) } if !a.testApex && len(a.properties.Unwanted_transitive_deps) > 0 { validations = append(validations, runApexElfCheckerUnwanted(ctx, unsignedOutputFile, a.properties.Unwanted_transitive_deps)) } - if !a.testApex && android.InList(a.payloadFsType, []fsType{ext4, erofs}) { + if !a.skipValidation(hostApexVerifier) && android.InList(a.payloadFsType, []fsType{ext4, erofs}) { validations = append(validations, runApexHostVerifier(ctx, a, unsignedOutputFile)) } ctx.Build(pctx, android.BuildParams{ @@ -1220,7 +1220,7 @@ func runApexLinkerconfigValidation(ctx android.ModuleContext, apexFile android.P // $ deapexer list -Z {apex_file} > {file_contexts} // $ apex_sepolicy_tests -f {file_contexts} func runApexSepolicyTests(ctx android.ModuleContext, apexFile android.Path) android.Path { - timestamp := android.PathForModuleOut(ctx, "sepolicy_tests.timestamp") + timestamp := android.PathForModuleOut(ctx, "apex_sepolicy_tests.timestamp") ctx.Build(pctx, android.BuildParams{ Rule: apexSepolicyTestsRule, Input: apexFile, diff --git a/apex/prebuilt.go b/apex/prebuilt.go index 2bef0cccf..f93eada8b 100644 --- a/apex/prebuilt.go +++ b/apex/prebuilt.go @@ -356,7 +356,6 @@ func (p *prebuiltCommon) apexInfoMutator(mctx android.TopDownMutatorContext) { apexInfo := android.ApexInfo{ ApexVariationName: apexVariationName, InApexVariants: []string{apexVariationName}, - InApexModules: []string{p.BaseModuleName()}, // BaseModuleName() to avoid the prebuilt_ prefix. ForPrebuiltApex: true, } diff --git a/bpf/libbpf/libbpf_prog.go b/bpf/libbpf/libbpf_prog.go index 18e03a5e7..3b26d4654 100644 --- a/bpf/libbpf/libbpf_prog.go +++ b/bpf/libbpf/libbpf_prog.go @@ -158,7 +158,8 @@ func (libbpf *libbpfProg) GenerateAndroidBuildActions(ctx android.ModuleContext) "-Wall", "-Werror", "-Wextra", - + // Flag to assist with the transition to libbpf + "-DENABLE_LIBBPF", "-isystem bionic/libc/include", "-isystem bionic/libc/kernel/uapi", // The architecture doesn't matter here, but asm/types.h is included by linux/types.h. diff --git a/cc/api_level.go b/cc/api_level.go index 69a0d3ae4..3dac571a9 100644 --- a/cc/api_level.go +++ b/cc/api_level.go @@ -41,12 +41,25 @@ func MinApiForArch(ctx android.EarlyModuleContext, } } +// Native API levels cannot be less than the MinApiLevelForArch. This function +// sets the lower bound of the API level with the MinApiLevelForArch. +func nativeClampedApiLevel(ctx android.BaseModuleContext, + apiLevel android.ApiLevel) android.ApiLevel { + + min := MinApiForArch(ctx, ctx.Arch().ArchType) + + if apiLevel.LessThan(min) { + return min + } + + return apiLevel +} + func nativeApiLevelFromUser(ctx android.BaseModuleContext, raw string) (android.ApiLevel, error) { - min := MinApiForArch(ctx, ctx.Arch().ArchType) if raw == "minimum" { - return min, nil + return MinApiForArch(ctx, ctx.Arch().ArchType), nil } value, err := android.ApiLevelFromUser(ctx, raw) @@ -54,15 +67,12 @@ func nativeApiLevelFromUser(ctx android.BaseModuleContext, return android.NoneApiLevel, err } - if value.LessThan(min) { - return min, nil - } - - return value, nil + return nativeClampedApiLevel(ctx, value), nil } func nativeApiLevelOrPanic(ctx android.BaseModuleContext, raw string) android.ApiLevel { + value, err := nativeApiLevelFromUser(ctx, raw) if err != nil { panic(err.Error()) diff --git a/cc/builder.go b/cc/builder.go index 2948ca316..b98bef9be 100644 --- a/cc/builder.go +++ b/cc/builder.go @@ -945,7 +945,7 @@ func transformObjToDynamicBinary(ctx android.ModuleContext, func transformDumpToLinkedDump(ctx android.ModuleContext, sAbiDumps android.Paths, soFile android.Path, baseName string, exportedIncludeDirs []string, symbolFile android.OptionalPath, excludedSymbolVersions, excludedSymbolTags, includedSymbolTags []string, - api string, isLlndk bool) android.Path { + api string) android.Path { outputFile := android.PathForModuleOut(ctx, baseName+".lsdump") @@ -966,9 +966,6 @@ func transformDumpToLinkedDump(ctx android.ModuleContext, sAbiDumps android.Path for _, tag := range includedSymbolTags { symbolFilterStr += " --include-symbol-tag " + tag } - if isLlndk { - symbolFilterStr += " --symbol-tag-policy MatchTagOnly" - } apiLevelsJson := android.GetApiLevelsJson(ctx) implicits = append(implicits, apiLevelsJson) symbolFilterStr += " --api-map " + apiLevelsJson.String() @@ -341,15 +341,14 @@ type BaseProperties struct { // If true, always create an sdk variant and don't create a platform variant. Sdk_variant_only *bool - AndroidMkSharedLibs []string `blueprint:"mutated"` - AndroidMkStaticLibs []string `blueprint:"mutated"` - AndroidMkRlibs []string `blueprint:"mutated"` - AndroidMkRuntimeLibs []string `blueprint:"mutated"` - AndroidMkWholeStaticLibs []string `blueprint:"mutated"` - AndroidMkHeaderLibs []string `blueprint:"mutated"` - HideFromMake bool `blueprint:"mutated"` - PreventInstall bool `blueprint:"mutated"` - ApexesProvidingSharedLibs []string `blueprint:"mutated"` + AndroidMkSharedLibs []string `blueprint:"mutated"` + AndroidMkStaticLibs []string `blueprint:"mutated"` + AndroidMkRlibs []string `blueprint:"mutated"` + AndroidMkRuntimeLibs []string `blueprint:"mutated"` + AndroidMkWholeStaticLibs []string `blueprint:"mutated"` + AndroidMkHeaderLibs []string `blueprint:"mutated"` + HideFromMake bool `blueprint:"mutated"` + PreventInstall bool `blueprint:"mutated"` // Set by DepsMutator. AndroidMkSystemSharedLibs []string `blueprint:"mutated"` @@ -1880,7 +1879,7 @@ var ( // Returns true if a stub library could be installed in multiple apexes func (c *Module) stubLibraryMultipleApexViolation(ctx android.ModuleContext) bool { // If this is not an apex variant, no check necessary - if !c.InAnyApex() { + if info, ok := android.ModuleProvider(ctx, android.ApexInfoProvider); !ok || info.IsForPlatform() { return false } // If this is not a stub library, no check necessary @@ -3285,18 +3284,6 @@ func (c *Module) depsToPaths(ctx android.ModuleContext) PathDeps { c.Properties.AndroidMkHeaderLibs = append( c.Properties.AndroidMkHeaderLibs, makeLibName) case libDepTag.shared(): - if lib := moduleLibraryInterface(dep); lib != nil { - if lib.buildStubs() && dep.(android.ApexModule).InAnyApex() { - // Add the dependency to the APEX(es) providing the library so that - // m <module> can trigger building the APEXes as well. - depApexInfo, _ := android.OtherModuleProvider(ctx, dep, android.ApexInfoProvider) - for _, an := range depApexInfo.InApexVariants { - c.Properties.ApexesProvidingSharedLibs = append( - c.Properties.ApexesProvidingSharedLibs, an) - } - } - } - // Note: the order of libs in this list is not important because // they merely serve as Make dependencies and do not affect this lib itself. c.Properties.AndroidMkSharedLibs = append( diff --git a/cc/cc_test.go b/cc/cc_test.go index 144b90b94..98af7b655 100644 --- a/cc/cc_test.go +++ b/cc/cc_test.go @@ -40,9 +40,6 @@ func TestMain(m *testing.M) { var prepareForCcTest = android.GroupFixturePreparers( PrepareForIntegrationTestWithCc, - android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) { - variables.VendorApiLevel = StringPtr("202404") - }), ) var apexVariationName = "apex28" @@ -1008,7 +1005,7 @@ func TestLlndkLibrary(t *testing.T) { android.AssertArrayString(t, "variants for llndk stubs", expected, actual) params := result.ModuleForTests("libllndk", "android_vendor_arm_armv7-a-neon_shared").Description("generate stub") - android.AssertSame(t, "use Vendor API level for default stubs", "999999", params.Args["apiLevel"]) + android.AssertSame(t, "use Vendor API level for default stubs", "35", params.Args["apiLevel"]) checkExportedIncludeDirs := func(module, variant string, expectedSystemDirs []string, expectedDirs ...string) { t.Helper() diff --git a/cc/config/global.go b/cc/config/global.go index 6984ea412..27aac959a 100644 --- a/cc/config/global.go +++ b/cc/config/global.go @@ -296,6 +296,7 @@ var ( // Allow using VLA CXX extension. "-Wno-vla-cxx-extension", + "-Wno-cast-function-type-mismatch", } noOverride64GlobalCflags = []string{} @@ -384,7 +385,7 @@ var ( // prebuilts/clang default settings. ClangDefaultBase = "prebuilts/clang/host" - ClangDefaultVersion = "clang-r530567" + ClangDefaultVersion = "clang-r536225" ClangDefaultShortVersion = "19" // Directories with warnings from Android.bp files. diff --git a/cc/library.go b/cc/library.go index 23ee9b141..ea8794644 100644 --- a/cc/library.go +++ b/cc/library.go @@ -566,10 +566,16 @@ func (library *libraryDecorator) getHeaderAbiCheckerProperties(m *Module) header func (library *libraryDecorator) compile(ctx ModuleContext, flags Flags, deps PathDeps) Objects { if ctx.IsLlndk() { - futureVendorApiLevel := android.ApiLevelOrPanic(ctx, "999999") + // Get the matching SDK version for the vendor API level. + version, err := android.GetSdkVersionForVendorApiLevel(ctx.Config().VendorApiLevel()) + if err != nil { + panic(err) + } + + // This is the vendor variant of an LLNDK library, build the LLNDK stubs. nativeAbiResult := parseNativeAbiDefinition(ctx, String(library.Properties.Llndk.Symbol_file), - futureVendorApiLevel, "--llndk") + nativeClampedApiLevel(ctx, version), "--llndk") objs := compileStubLibrary(ctx, flags, nativeAbiResult.stubSrc) if !Bool(library.Properties.Llndk.Unversioned) { library.versionScriptPath = android.OptionalPathForPath( @@ -1285,15 +1291,14 @@ func (library *libraryDecorator) llndkIncludeDirsForAbiCheck(ctx ModuleContext, func (library *libraryDecorator) linkLlndkSAbiDumpFiles(ctx ModuleContext, deps PathDeps, sAbiDumpFiles android.Paths, soFile android.Path, libFileName string, excludeSymbolVersions, excludeSymbolTags []string, - vendorApiLevel string) android.Path { - // NDK symbols in version 34 are LLNDK symbols. Those in version 35 are not. + sdkVersionForVendorApiLevel string) android.Path { return transformDumpToLinkedDump(ctx, sAbiDumpFiles, soFile, libFileName+".llndk", library.llndkIncludeDirsForAbiCheck(ctx, deps), android.OptionalPathForModuleSrc(ctx, library.Properties.Llndk.Symbol_file), append([]string{"*_PLATFORM", "*_PRIVATE"}, excludeSymbolVersions...), append([]string{"platform-only"}, excludeSymbolTags...), - []string{"llndk=" + vendorApiLevel}, "34", true /* isLlndk */) + []string{"llndk"}, sdkVersionForVendorApiLevel) } func (library *libraryDecorator) linkApexSAbiDumpFiles(ctx ModuleContext, @@ -1306,7 +1311,7 @@ func (library *libraryDecorator) linkApexSAbiDumpFiles(ctx ModuleContext, android.OptionalPathForModuleSrc(ctx, library.Properties.Stubs.Symbol_file), append([]string{"*_PLATFORM", "*_PRIVATE"}, excludeSymbolVersions...), append([]string{"platform-only"}, excludeSymbolTags...), - []string{"apex", "systemapi"}, sdkVersion, false /* isLlndk */) + []string{"apex", "systemapi"}, sdkVersion) } func getRefAbiDumpFile(ctx android.ModuleInstallPathContext, @@ -1444,7 +1449,7 @@ func (library *libraryDecorator) linkSAbiDumpFiles(ctx ModuleContext, deps PathD android.OptionalPathForModuleSrc(ctx, library.symbolFileForAbiCheck(ctx)), headerAbiChecker.Exclude_symbol_versions, headerAbiChecker.Exclude_symbol_tags, - []string{} /* includeSymbolTags */, currSdkVersion, false /* isLlndk */) + []string{} /* includeSymbolTags */, currSdkVersion) var llndkDump, apexVariantDump android.Path tags := classifySourceAbiDump(ctx.Module().(*Module)) @@ -1452,12 +1457,17 @@ func (library *libraryDecorator) linkSAbiDumpFiles(ctx ModuleContext, deps PathD for _, tag := range tags { if tag == llndkLsdumpTag && currVendorVersion != "" { if llndkDump == nil { + sdkVersion, err := android.GetSdkVersionForVendorApiLevel(currVendorVersion) + if err != nil { + ctx.ModuleErrorf("Cannot create %s llndk dump: %s", fileName, err) + return + } // TODO(b/323447559): Evaluate if replacing sAbiDumpFiles with implDump is faster llndkDump = library.linkLlndkSAbiDumpFiles(ctx, deps, objs.sAbiDumpFiles, soFile, fileName, headerAbiChecker.Exclude_symbol_versions, headerAbiChecker.Exclude_symbol_tags, - currVendorVersion) + nativeClampedApiLevel(ctx, sdkVersion).String()) } addLsdumpPath(ctx.Config(), string(tag)+":"+llndkDump.String()) } else if tag == apexLsdumpTag { diff --git a/cc/linker.go b/cc/linker.go index f9d58ea20..b96d13983 100644 --- a/cc/linker.go +++ b/cc/linker.go @@ -235,13 +235,16 @@ type BaseLinkerProperties struct { // Generate compact dynamic relocation table, default true. Pack_relocations *bool `android:"arch_variant"` - // local file name to pass to the linker as --version-script + // local file name to pass to the linker as --version-script. Not supported on darwin, and will fail to build + // if provided to the darwin variant of a module. Version_script *string `android:"path,arch_variant"` - // local file name to pass to the linker as --dynamic-list + // local file name to pass to the linker as --dynamic-list. Not supported on darwin, and will fail to build + // if provided to the darwin variant of a module. Dynamic_list *string `android:"path,arch_variant"` - // local files to pass to the linker as --script + // local files to pass to the linker as --script. Not supported on darwin or windows, and will fail to build + // if provided to the darwin or windows variant of a module. Linker_scripts []string `android:"path,arch_variant"` // list of static libs that should not be used to build this module @@ -560,7 +563,7 @@ func (linker *baseLinker) linkerFlags(ctx ModuleContext, flags Flags) Flags { if versionScript.Valid() { if ctx.Darwin() { - ctx.PropertyErrorf("version_script", "Not supported on Darwin") + ctx.AddMissingDependencies([]string{"version_script_not_supported_on_darwin"}) } else { flags.Local.LdFlags = append(flags.Local.LdFlags, config.VersionScriptFlagPrefix+versionScript.String()) @@ -578,7 +581,7 @@ func (linker *baseLinker) linkerFlags(ctx ModuleContext, flags Flags) Flags { dynamicList := android.OptionalPathForModuleSrc(ctx, linker.Properties.Dynamic_list) if dynamicList.Valid() { if ctx.Darwin() { - ctx.PropertyErrorf("dynamic_list", "Not supported on Darwin") + ctx.AddMissingDependencies([]string{"dynamic_list_not_supported_on_darwin"}) } else { flags.Local.LdFlags = append(flags.Local.LdFlags, "-Wl,--dynamic-list,"+dynamicList.String()) @@ -587,13 +590,17 @@ func (linker *baseLinker) linkerFlags(ctx ModuleContext, flags Flags) Flags { } linkerScriptPaths := android.PathsForModuleSrc(ctx, linker.Properties.Linker_scripts) - if len(linkerScriptPaths) > 0 && (ctx.Darwin() || ctx.Windows()) { - ctx.PropertyErrorf("linker_scripts", "Only supported for ELF files") - } else { - for _, linkerScriptPath := range linkerScriptPaths { - flags.Local.LdFlags = append(flags.Local.LdFlags, - "-Wl,--script,"+linkerScriptPath.String()) - flags.LdFlagsDeps = append(flags.LdFlagsDeps, linkerScriptPath) + if len(linkerScriptPaths) > 0 { + if ctx.Darwin() { + ctx.AddMissingDependencies([]string{"linker_scripts_not_supported_on_darwin"}) + } else if ctx.Windows() { + ctx.PropertyErrorf("linker_scripts", "Only supported for ELF files") + } else { + for _, linkerScriptPath := range linkerScriptPaths { + flags.Local.LdFlags = append(flags.Local.LdFlags, + "-Wl,--script,"+linkerScriptPath.String()) + flags.LdFlagsDeps = append(flags.LdFlagsDeps, linkerScriptPath) + } } } } diff --git a/cc/ndkstubgen/test_ndkstubgen.py b/cc/ndkstubgen/test_ndkstubgen.py index 22f31d9f1..6c24b4f19 100755 --- a/cc/ndkstubgen/test_ndkstubgen.py +++ b/cc/ndkstubgen/test_ndkstubgen.py @@ -473,16 +473,17 @@ class IntegrationTest(unittest.TestCase): VERSION_35 { # introduced=35 global: wiggle; - waggle; - waggle; # llndk=202404 - bubble; # llndk=202404 - duddle; - duddle; # llndk=202504 + waggle; # llndk } VERSION_34; + VERSION_36 { # introduced=36 + global: + abc; + xyz; # llndk + } VERSION_35; """)) f = copy(self.filter) f.llndk = True - f.api = 202404 + f.api = 35 parser = symbolfile.SymbolFileParser(input_file, {}, f) versions = parser.parse() @@ -497,8 +498,8 @@ class IntegrationTest(unittest.TestCase): expected_src = textwrap.dedent("""\ void foo() {} void bar() {} + void wiggle() {} void waggle() {} - void bubble() {} """) self.assertEqual(expected_src, src_file.getvalue()) @@ -510,8 +511,8 @@ class IntegrationTest(unittest.TestCase): }; VERSION_35 { global: + wiggle; waggle; - bubble; } VERSION_34; """) self.assertEqual(expected_version, version_file.getvalue()) @@ -521,15 +522,15 @@ class IntegrationTest(unittest.TestCase): LIBANDROID { global: foo; # introduced=34 - bar; # introduced=35 - bar; # llndk=202404 - baz; # introduced=35 + bar; # introduced=35 llndk + baz; # introduced=V + qux; # introduced=36 }; """)) f = copy(self.filter) f.llndk = True - f.api = 202404 - parser = symbolfile.SymbolFileParser(input_file, {}, f) + f.api = 35 + parser = symbolfile.SymbolFileParser(input_file, {'V': 35}, f) versions = parser.parse() src_file = io.StringIO() @@ -543,6 +544,7 @@ class IntegrationTest(unittest.TestCase): expected_src = textwrap.dedent("""\ void foo() {} void bar() {} + void baz() {} """) self.assertEqual(expected_src, src_file.getvalue()) @@ -551,6 +553,7 @@ class IntegrationTest(unittest.TestCase): global: foo; bar; + baz; }; """) self.assertEqual(expected_version, version_file.getvalue()) diff --git a/cc/symbolfile/__init__.py b/cc/symbolfile/__init__.py index 4553616ac..f2bd18690 100644 --- a/cc/symbolfile/__init__.py +++ b/cc/symbolfile/__init__.py @@ -103,24 +103,13 @@ class Tags: @property def has_llndk_tags(self) -> bool: """Returns True if any LL-NDK tags are set.""" - for tag in self.tags: - if tag == 'llndk' or tag.startswith('llndk='): - return True - return False + return 'llndk' in self.tags @property def has_platform_only_tags(self) -> bool: """Returns True if any platform-only tags are set.""" return 'platform-only' in self.tags - def copy_introduced_from(self, tags: Tags) -> None: - """Copies introduced= or introduced-*= tags.""" - for tag in tags: - if tag.startswith('introduced=') or tag.startswith('introduced-'): - name, _ = split_tag(tag) - if not any(self_tag.startswith(name + '=') for self_tag in self.tags): - self.tags += (tag,) - @dataclass class Symbol: @@ -158,8 +147,6 @@ def is_api_level_tag(tag: Tag) -> bool: """Returns true if this tag has an API level that may need decoding.""" if tag.startswith('llndk-deprecated='): return True - if tag.startswith('llndk='): - return True if tag.startswith('introduced='): return True if tag.startswith('introduced-'): @@ -245,21 +232,19 @@ class Filter: self.systemapi = systemapi self.ndk = ndk + def _symbol_in_arch_api(self, tags: Tags) -> bool: + if not symbol_in_arch(tags, self.arch): + return True + if not symbol_in_api(tags, self.arch, self.api): + return True + return False + def _should_omit_tags(self, tags: Tags) -> bool: """Returns True if the tagged object should be omitted. This defines the rules shared between version tagging and symbol tagging. """ - # LLNDK mode/tags follow the similar filtering except that API level checking - # is based llndk= instead of introduced=. - if self.llndk: - if tags.has_mode_tags and not tags.has_llndk_tags: - return True - if not symbol_in_arch(tags, self.arch): - return True - if not symbol_in_llndk_api(tags, self.arch, self.api): - return True - return False + # The apex and llndk tags will only exclude APIs from other modes. If in # APEX or LLNDK mode and neither tag is provided, we fall back to the # default behavior because all NDK symbols are implicitly available to # APEX and LLNDK. @@ -268,12 +253,10 @@ class Filter: return False if self.systemapi and tags.has_systemapi_tags: return False + if self.llndk and tags.has_llndk_tags: + return self._symbol_in_arch_api(tags) return True - if not symbol_in_arch(tags, self.arch): - return True - if not symbol_in_api(tags, self.arch, self.api): - return True - return False + return self._symbol_in_arch_api(tags) def should_omit_version(self, version: Version) -> bool: """Returns True if the version section should be omitted. @@ -286,10 +269,6 @@ class Filter: return True if version.tags.has_platform_only_tags: return True - # Include all versions when targeting LLNDK because LLNDK symbols are self-versioned. - # Empty version block will be handled separately. - if self.llndk: - return False return self._should_omit_tags(version.tags) def should_omit_symbol(self, symbol: Symbol) -> bool: @@ -302,6 +281,7 @@ class Filter: return self._should_omit_tags(symbol.tags) + def symbol_in_arch(tags: Tags, arch: Arch) -> bool: """Returns true if the symbol is present for the given architecture.""" has_arch_tags = False @@ -316,14 +296,6 @@ def symbol_in_arch(tags: Tags, arch: Arch) -> bool: # for the tagged architectures. return not has_arch_tags -def symbol_in_llndk_api(tags: Iterable[Tag], arch: Arch, api: int) -> bool: - """Returns true if the symbol is present for the given LLNDK API level.""" - # Check llndk= first. - for tag in tags: - if tag.startswith('llndk='): - return api >= int(get_tag_value(tag)) - # If not, we keep old behavior: NDK symbols in <= 34 are LLNDK symbols. - return symbol_in_api(tags, arch, 34) def symbol_in_api(tags: Iterable[Tag], arch: Arch, api: int) -> bool: """Returns true if the symbol is present for the given API level.""" @@ -400,7 +372,6 @@ class SymbolFileParser: f'Unexpected contents at top level: {self.current_line}') self.check_no_duplicate_symbols(versions) - self.check_llndk_introduced(versions) return versions def check_no_duplicate_symbols(self, versions: Iterable[Version]) -> None: @@ -429,31 +400,6 @@ class SymbolFileParser: raise MultiplyDefinedSymbolError( sorted(list(multiply_defined_symbols))) - def check_llndk_introduced(self, versions: Iterable[Version]) -> None: - """Raises errors when llndk= is missing for new llndk symbols.""" - if not self.filter.llndk: - return - - def assert_llndk_with_version(tags: Tags, name: str) -> None: - has_llndk_introduced = False - for tag in tags: - if tag.startswith('llndk='): - has_llndk_introduced = True - break - if not has_llndk_introduced: - raise ParseError(f'{name}: missing version. `llndk=yyyymm`') - - arch = self.filter.arch - for version in versions: - # llndk symbols >= introduced=35 should be tagged - # explicitly with llndk=yyyymm. - for symbol in version.symbols: - if not symbol.tags.has_llndk_tags: - continue - if symbol_in_api(symbol.tags, arch, 34): - continue - assert_llndk_with_version(symbol.tags, symbol.name) - def parse_version(self) -> Version: """Parses a single version section and returns a Version object.""" assert self.current_line is not None @@ -487,9 +433,7 @@ class SymbolFileParser: else: raise ParseError('Unknown visiblity label: ' + visibility) elif global_scope and not cpp_symbols: - symbol = self.parse_symbol() - symbol.tags.copy_introduced_from(tags) - symbols.append(symbol) + symbols.append(self.parse_symbol()) else: # We're in a hidden scope or in 'extern "C++"' block. Ignore # everything. diff --git a/cc/symbolfile/test_symbolfile.py b/cc/symbolfile/test_symbolfile.py index 8b412b98a..14bb737ee 100644 --- a/cc/symbolfile/test_symbolfile.py +++ b/cc/symbolfile/test_symbolfile.py @@ -344,45 +344,6 @@ class OmitSymbolTest(unittest.TestCase): self.assertInclude(f_llndk, s_none) self.assertInclude(f_llndk, s_llndk) - def test_omit_llndk_versioned(self) -> None: - f_ndk = self.filter - f_ndk.api = 35 - - f_llndk = copy(f_ndk) - f_llndk.llndk = True - f_llndk.api = 202404 - - s = Symbol('foo', Tags()) - s_llndk = Symbol('foo', Tags.from_strs(['llndk'])) - s_llndk_202404 = Symbol('foo', Tags.from_strs(['llndk=202404'])) - s_34 = Symbol('foo', Tags.from_strs(['introduced=34'])) - s_34_llndk = Symbol('foo', Tags.from_strs(['introduced=34', 'llndk'])) - s_35 = Symbol('foo', Tags.from_strs(['introduced=35'])) - s_35_llndk_202404 = Symbol('foo', Tags.from_strs(['introduced=35', 'llndk=202404'])) - s_35_llndk_202504 = Symbol('foo', Tags.from_strs(['introduced=35', 'llndk=202504'])) - - # When targeting NDK, omit LLNDK tags - self.assertInclude(f_ndk, s) - self.assertOmit(f_ndk, s_llndk) - self.assertOmit(f_ndk, s_llndk_202404) - self.assertInclude(f_ndk, s_34) - self.assertOmit(f_ndk, s_34_llndk) - self.assertInclude(f_ndk, s_35) - self.assertOmit(f_ndk, s_35_llndk_202404) - self.assertOmit(f_ndk, s_35_llndk_202504) - - # When targeting LLNDK, old symbols without any mode tags are included as LLNDK - self.assertInclude(f_llndk, s) - # When targeting LLNDK, old symbols with #llndk are included as LLNDK - self.assertInclude(f_llndk, s_llndk) - self.assertInclude(f_llndk, s_llndk_202404) - self.assertInclude(f_llndk, s_34) - self.assertInclude(f_llndk, s_34_llndk) - # When targeting LLNDK, new symbols(>=35) should be tagged with llndk-introduced=. - self.assertOmit(f_llndk, s_35) - self.assertInclude(f_llndk, s_35_llndk_202404) - self.assertOmit(f_llndk, s_35_llndk_202504) - def test_omit_apex(self) -> None: f_none = self.filter f_apex = copy(f_none) @@ -494,8 +455,8 @@ class SymbolFileParseTest(unittest.TestCase): # should_omit_tags() can differently based on introduced API level when treating # LLNDK-available symbols. expected_symbols = [ - Symbol('baz', Tags.from_strs(['introduced=35'])), - Symbol('qux', Tags.from_strs(['apex', 'llndk', 'introduced=35'])), + Symbol('baz', Tags()), + Symbol('qux', Tags.from_strs(['apex', 'llndk'])), ] self.assertEqual(expected_symbols, version.symbols) @@ -643,19 +604,6 @@ class SymbolFileParseTest(unittest.TestCase): ] self.assertEqual(expected_symbols, version.symbols) - def test_parse_llndk_version_is_missing(self) -> None: - input_file = io.StringIO(textwrap.dedent("""\ - VERSION_1 { # introduced=35 - foo; - bar; # llndk - }; - """)) - f = copy(self.filter) - f.llndk = True - parser = symbolfile.SymbolFileParser(input_file, {}, f) - with self.assertRaises(symbolfile.ParseError): - parser.parse() - def main() -> None: suite = unittest.TestLoader().loadTestsFromName(__name__) diff --git a/filesystem/bootimg.go b/filesystem/bootimg.go index 22495064d..0ffec2654 100644 --- a/filesystem/bootimg.go +++ b/filesystem/bootimg.go @@ -80,6 +80,12 @@ type BootimgProperties struct { // When set to true, sign the image with avbtool. Default is false. Use_avb *bool + // This can either be "default", or "make_legacy". "make_legacy" will sign the boot image + // like how build/make/core/Makefile does, to get bit-for-bit backwards compatibility. But + // we may want to reconsider if it's necessary to have two modes in the future. The default + // is "default" + Avb_mode *string + // Name of the partition stored in vbmeta desc. Defaults to the name of this module. Partition_name *string @@ -89,6 +95,13 @@ type BootimgProperties struct { // Hash and signing algorithm for avbtool. Default is SHA256_RSA4096. Avb_algorithm *string + + // The index used to prevent rollback of the image on device. + Avb_rollback_index *int64 + + // The security patch passed to as the com.android.build.<type>.security_patch avb property. + // Replacement for the make variables BOOT_SECURITY_PATCH / INIT_BOOT_SECURITY_PATCH. + Security_patch *string } type bootImageType int @@ -114,6 +127,19 @@ func toBootImageType(ctx android.ModuleContext, bootImageType string) bootImageT return unsupported } +func (b bootImageType) String() string { + switch b { + case boot: + return "boot" + case vendorBoot: + return "vendor_boot" + case initBoot: + return "init_boot" + default: + panic("unknown boot image type") + } +} + func (b bootImageType) isBoot() bool { return b == boot } @@ -158,11 +184,39 @@ func (b *bootimg) partitionName() string { func (b *bootimg) GenerateAndroidBuildActions(ctx android.ModuleContext) { b.bootImageType = toBootImageType(ctx, proptools.StringDefault(b.properties.Boot_image_type, "boot")) - unsignedOutput := b.buildBootImage(ctx) + if b.bootImageType == unsupported { + return + } + + kernelProp := proptools.String(b.properties.Kernel_prebuilt) + if b.bootImageType.isVendorBoot() && kernelProp != "" { + ctx.PropertyErrorf("kernel_prebuilt", "vendor_boot partition can't have kernel") + return + } + if b.bootImageType.isBoot() && kernelProp == "" { + ctx.PropertyErrorf("kernel_prebuilt", "boot partition must have kernel") + return + } + var kernel android.Path + if kernelProp != "" { + kernel = android.PathForModuleSrc(ctx, kernelProp) + } + + unsignedOutput := b.buildBootImage(ctx, kernel) output := unsignedOutput if proptools.Bool(b.properties.Use_avb) { - output = b.signImage(ctx, unsignedOutput) + // This bootimg module supports 2 modes of avb signing. It is not clear to this author + // why there are differences, but one of them is to match the behavior of make-built boot + // images. + switch proptools.StringDefault(b.properties.Avb_mode, "default") { + case "default": + output = b.signImage(ctx, unsignedOutput) + case "make_legacy": + output = b.addAvbFooter(ctx, unsignedOutput, kernel) + default: + ctx.PropertyErrorf("avb_mode", `Unknown value for avb_mode, expected "default" or "make_legacy", got: %q`, *b.properties.Avb_mode) + } } b.installDir = android.PathForModuleInstall(ctx, "etc") @@ -172,24 +226,14 @@ func (b *bootimg) GenerateAndroidBuildActions(ctx android.ModuleContext) { b.output = output } -func (b *bootimg) buildBootImage(ctx android.ModuleContext) android.Path { +func (b *bootimg) buildBootImage(ctx android.ModuleContext, kernel android.Path) android.Path { output := android.PathForModuleOut(ctx, "unsigned", b.installFileName()) builder := android.NewRuleBuilder(pctx, ctx) cmd := builder.Command().BuiltTool("mkbootimg") - kernel := proptools.String(b.properties.Kernel_prebuilt) - if b.bootImageType.isVendorBoot() && kernel != "" { - ctx.PropertyErrorf("kernel_prebuilt", "vendor_boot partition can't have kernel") - return output - } - - if b.bootImageType.isBoot() && kernel == "" { - ctx.PropertyErrorf("kernel_prebuilt", "boot partition must have kernel") - return output - } - if kernel != "" { - cmd.FlagWithInput("--kernel ", android.PathForModuleSrc(ctx, kernel)) + if kernel != nil { + cmd.FlagWithInput("--kernel ", kernel) } // These arguments are passed for boot.img and init_boot.img generation @@ -272,6 +316,66 @@ func (b *bootimg) buildBootImage(ctx android.ModuleContext) android.Path { return output } +func (b *bootimg) addAvbFooter(ctx android.ModuleContext, unsignedImage android.Path, kernel android.Path) android.Path { + output := android.PathForModuleOut(ctx, b.installFileName()) + builder := android.NewRuleBuilder(pctx, ctx) + builder.Command().Text("cp").Input(unsignedImage).Output(output) + cmd := builder.Command().BuiltTool("avbtool"). + Text("add_hash_footer"). + FlagWithInput("--image ", output) + + if b.properties.Partition_size != nil { + cmd.FlagWithArg("--partition_size ", strconv.FormatInt(*b.properties.Partition_size, 10)) + } else { + cmd.Flag("--dynamic_partition_size") + } + + // If you don't provide a salt, avbtool will use random bytes for the salt. + // This is bad for determinism (cached builds and diff tests are affected), so instead, + // we try to provide a salt. The requirements for a salt are not very clear, one aspect of it + // is that if it's unpredictable, attackers trying to change the contents of a partition need + // to find a new hash collision every release, because the salt changed. + if kernel != nil { + cmd.Textf(`--salt $(sha256sum "%s" | cut -d " " -f 1)`, kernel.String()) + cmd.Implicit(kernel) + } else { + cmd.Textf(`--salt $(sha256sum "%s" "%s" | cut -d " " -f 1 | tr -d '\n')`, ctx.Config().BuildNumberFile(ctx), ctx.Config().Getenv("BUILD_DATETIME_FILE")) + cmd.OrderOnly(ctx.Config().BuildNumberFile(ctx)) + } + + cmd.FlagWithArg("--partition_name ", b.bootImageType.String()) + + if b.properties.Avb_algorithm != nil { + cmd.FlagWithArg("--algorithm ", proptools.NinjaAndShellEscape(*b.properties.Avb_algorithm)) + } + + if b.properties.Avb_private_key != nil { + key := android.PathForModuleSrc(ctx, proptools.String(b.properties.Avb_private_key)) + cmd.FlagWithInput("--key ", key) + } + + if !b.bootImageType.isVendorBoot() { + cmd.FlagWithArg("--prop ", proptools.NinjaAndShellEscape(fmt.Sprintf( + "com.android.build.%s.os_version:%s", b.bootImageType.String(), ctx.Config().PlatformVersionLastStable()))) + } + + fingerprintFile := ctx.Config().BuildFingerprintFile(ctx) + cmd.FlagWithArg("--prop ", fmt.Sprintf("com.android.build.%s.fingerprint:$(cat %s)", b.bootImageType.String(), fingerprintFile.String())) + cmd.OrderOnly(fingerprintFile) + + if b.properties.Security_patch != nil { + cmd.FlagWithArg("--prop ", proptools.NinjaAndShellEscape(fmt.Sprintf( + "com.android.build.%s.security_patch:%s", b.bootImageType.String(), *b.properties.Security_patch))) + } + + if b.properties.Avb_rollback_index != nil { + cmd.FlagWithArg("--rollback_index ", strconv.FormatInt(*b.properties.Avb_rollback_index, 10)) + } + + builder.Build("add_avb_footer", fmt.Sprintf("Adding avb footer to %s", b.BaseModuleName())) + return output +} + func (b *bootimg) signImage(ctx android.ModuleContext, unsignedImage android.Path) android.Path { propFile, toolDeps := b.buildPropFile(ctx) diff --git a/filesystem/filesystem.go b/filesystem/filesystem.go index dadacae3d..9348f91d4 100644 --- a/filesystem/filesystem.go +++ b/filesystem/filesystem.go @@ -71,6 +71,10 @@ type filesystemBuilder interface { // For example, GSI system.img contains system_ext and product artifacts and their // relPathInPackage need to be rebased to system/system_ext and system/system_product. ModifyPackagingSpec(spec *android.PackagingSpec) + + // Function to check if the filesystem should not use `vintf_fragments` property, + // but use `vintf_fragment` module type instead + ShouldUseVintfFragmentModuleOnly() bool } var _ filesystemBuilder = (*filesystem)(nil) @@ -343,6 +347,9 @@ var pctx = android.NewPackageContext("android/soong/filesystem") func (f *filesystem) GenerateAndroidBuildActions(ctx android.ModuleContext) { validatePartitionType(ctx, f) + if f.filesystemBuilder.ShouldUseVintfFragmentModuleOnly() { + f.validateVintfFragments(ctx) + } switch f.fsType(ctx) { case ext4Type, erofsType, f2fsType: f.output = f.buildImageUsingBuildImage(ctx) @@ -371,6 +378,43 @@ func (f *filesystem) GenerateAndroidBuildActions(ctx android.ModuleContext) { } } +func (f *filesystem) validateVintfFragments(ctx android.ModuleContext) { + visitedModule := map[string]bool{} + packagingSpecs := f.gatherFilteredPackagingSpecs(ctx) + + moduleInFileSystem := func(mod android.Module) bool { + for _, ps := range android.OtherModuleProviderOrDefault( + ctx, mod, android.InstallFilesProvider).PackagingSpecs { + if _, ok := packagingSpecs[ps.RelPathInPackage()]; ok { + return true + } + } + return false + } + + ctx.WalkDeps(func(child, parent android.Module) bool { + if visitedModule[child.Name()] { + return false + } + if !moduleInFileSystem(child) { + visitedModule[child.Name()] = true + return true + } + if vintfFragments := child.VintfFragments(ctx); vintfFragments != nil { + ctx.PropertyErrorf( + "vintf_fragments", + "Module %s is referenced by soong-defined filesystem %s with property vintf_fragments(%s) in use."+ + " Use vintf_fragment_modules property instead.", + child.Name(), + f.BaseModuleName(), + strings.Join(vintfFragments, ", "), + ) + } + visitedModule[child.Name()] = true + return true + }) +} + func (f *filesystem) appendToEntry(ctx android.ModuleContext, installedFile android.Path) { partitionBaseDir := android.PathForModuleOut(ctx, "root", f.partitionName()).String() + "/" @@ -779,6 +823,10 @@ func (f *filesystem) BuildLinkerConfigFile(ctx android.ModuleContext, builder *a f.appendToEntry(ctx, output) } +func (f *filesystem) ShouldUseVintfFragmentModuleOnly() bool { + return false +} + type partition interface { PartitionType() string } diff --git a/filesystem/system_image.go b/filesystem/system_image.go index d03eab45b..60a513346 100644 --- a/filesystem/system_image.go +++ b/filesystem/system_image.go @@ -63,3 +63,7 @@ func (s *systemImage) FilterPackagingSpec(ps android.PackagingSpec) bool { (ps.Partition() == "system" || ps.Partition() == "root" || strings.HasPrefix(ps.Partition(), "system/")) } + +func (s *systemImage) ShouldUseVintfFragmentModuleOnly() bool { + return true +} diff --git a/fsgen/boot_imgs.go b/fsgen/boot_imgs.go index 630aaff00..799dbc9e3 100644 --- a/fsgen/boot_imgs.go +++ b/fsgen/boot_imgs.go @@ -6,11 +6,12 @@ import ( "fmt" "path/filepath" "strconv" + "strings" "github.com/google/blueprint/proptools" ) -func createBootImage(ctx android.LoadHookContext) bool { +func createBootImage(ctx android.LoadHookContext, dtbImg dtbImg) bool { partitionVariables := ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse if partitionVariables.TargetKernelPath == "" { @@ -38,21 +39,47 @@ func createBootImage(ctx android.LoadHookContext) bool { var partitionSize *int64 if partitionVariables.BoardBootimagePartitionSize != "" { - parsed, err := strconv.ParseInt(partitionVariables.BoardBootimagePartitionSize, 10, 64) + // Base of zero will allow base 10 or base 16 if starting with 0x + parsed, err := strconv.ParseInt(partitionVariables.BoardBootimagePartitionSize, 0, 64) if err != nil { panic(fmt.Sprintf("BOARD_BOOTIMAGE_PARTITION_SIZE must be an int, got %s", partitionVariables.BoardBootimagePartitionSize)) } partitionSize = &parsed } + var securityPatch *string + if partitionVariables.BootSecurityPatch != "" { + securityPatch = &partitionVariables.BootSecurityPatch + } + + avbInfo := getAvbInfo(ctx.Config(), "boot") + bootImageName := generatedModuleNameForPartition(ctx.Config(), "boot") + var dtbPrebuilt *string + if dtbImg.include && dtbImg.imgType == "boot" { + dtbPrebuilt = proptools.StringPtr(":" + dtbImg.name) + } + + var cmdline []string + if !buildingVendorBootImage(partitionVariables) { + cmdline = partitionVariables.InternalKernelCmdline + } + ctx.CreateModule( filesystem.BootimgFactory, &filesystem.BootimgProperties{ - Kernel_prebuilt: proptools.StringPtr(":" + kernelFilegroupName), - Header_version: proptools.StringPtr(partitionVariables.BoardBootHeaderVersion), - Partition_size: partitionSize, + Kernel_prebuilt: proptools.StringPtr(":" + kernelFilegroupName), + Header_version: proptools.StringPtr(partitionVariables.BoardBootHeaderVersion), + Partition_size: partitionSize, + Use_avb: avbInfo.avbEnable, + Avb_mode: avbInfo.avbMode, + Avb_private_key: avbInfo.avbkeyFilegroup, + Avb_rollback_index: avbInfo.avbRollbackIndex, + Avb_algorithm: avbInfo.avbAlgorithm, + Security_patch: securityPatch, + Dtb_prebuilt: dtbPrebuilt, + Cmdline: cmdline, }, &struct { Name *string @@ -63,17 +90,33 @@ func createBootImage(ctx android.LoadHookContext) bool { return true } -func createVendorBootImage(ctx android.LoadHookContext) bool { +func createVendorBootImage(ctx android.LoadHookContext, dtbImg dtbImg) bool { partitionVariables := ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse bootImageName := generatedModuleNameForPartition(ctx.Config(), "vendor_boot") + avbInfo := getAvbInfo(ctx.Config(), "vendor_boot") + + var dtbPrebuilt *string + if dtbImg.include && dtbImg.imgType == "vendor_boot" { + dtbPrebuilt = proptools.StringPtr(":" + dtbImg.name) + } + + cmdline := partitionVariables.InternalKernelCmdline + ctx.CreateModule( filesystem.BootimgFactory, &filesystem.BootimgProperties{ - Boot_image_type: proptools.StringPtr("vendor_boot"), - Ramdisk_module: proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "vendor_ramdisk")), - Header_version: proptools.StringPtr(partitionVariables.BoardBootHeaderVersion), + Boot_image_type: proptools.StringPtr("vendor_boot"), + Ramdisk_module: proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "vendor_ramdisk")), + Header_version: proptools.StringPtr(partitionVariables.BoardBootHeaderVersion), + Use_avb: avbInfo.avbEnable, + Avb_mode: avbInfo.avbMode, + Avb_private_key: avbInfo.avbkeyFilegroup, + Avb_rollback_index: avbInfo.avbRollbackIndex, + Avb_algorithm: avbInfo.avbAlgorithm, + Dtb_prebuilt: dtbPrebuilt, + Cmdline: cmdline, }, &struct { Name *string @@ -89,12 +132,38 @@ func createInitBootImage(ctx android.LoadHookContext) bool { bootImageName := generatedModuleNameForPartition(ctx.Config(), "init_boot") + var securityPatch *string + if partitionVariables.InitBootSecurityPatch != "" { + securityPatch = &partitionVariables.InitBootSecurityPatch + } else if partitionVariables.BootSecurityPatch != "" { + securityPatch = &partitionVariables.BootSecurityPatch + } + + var partitionSize *int64 + if partitionVariables.BoardInitBootimagePartitionSize != "" { + // Base of zero will allow base 10 or base 16 if starting with 0x + parsed, err := strconv.ParseInt(partitionVariables.BoardInitBootimagePartitionSize, 0, 64) + if err != nil { + panic(fmt.Sprintf("BOARD_INIT_BOOT_IMAGE_PARTITION_SIZE must be an int, got %s", partitionVariables.BoardInitBootimagePartitionSize)) + } + partitionSize = &parsed + } + + avbInfo := getAvbInfo(ctx.Config(), "init_boot") + ctx.CreateModule( filesystem.BootimgFactory, &filesystem.BootimgProperties{ - Boot_image_type: proptools.StringPtr("init_boot"), - Ramdisk_module: proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "ramdisk")), - Header_version: proptools.StringPtr(partitionVariables.BoardBootHeaderVersion), + Boot_image_type: proptools.StringPtr("init_boot"), + Ramdisk_module: proptools.StringPtr(generatedModuleNameForPartition(ctx.Config(), "ramdisk")), + Header_version: proptools.StringPtr(partitionVariables.BoardBootHeaderVersion), + Security_patch: securityPatch, + Partition_size: partitionSize, + Use_avb: avbInfo.avbEnable, + Avb_mode: avbInfo.avbMode, + Avb_private_key: avbInfo.avbkeyFilegroup, + Avb_rollback_index: avbInfo.avbRollbackIndex, + Avb_algorithm: avbInfo.avbAlgorithm, }, &struct { Name *string @@ -170,3 +239,47 @@ func boardBootHeaderVersion(partitionVars android.PartitionVariables) (int, bool } return int(v), true } + +type dtbImg struct { + // whether to include the dtb image in boot image + include bool + + // name of the generated dtb image filegroup name + name string + + // type of the boot image that the dtb image argument should be specified + imgType string +} + +func createDtbImgFilegroup(ctx android.LoadHookContext) dtbImg { + partitionVars := ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse + if !partitionVars.BoardIncludeDtbInBootimg { + return dtbImg{include: false} + } + for _, copyFilePair := range partitionVars.ProductCopyFiles { + srcDestList := strings.Split(copyFilePair, ":") + if len(srcDestList) < 2 { + ctx.ModuleErrorf("PRODUCT_COPY_FILES must follow the format \"src:dest\", got: %s", copyFilePair) + } + if srcDestList[1] == "dtb.img" { + moduleName := generatedModuleName(ctx.Config(), "dtb_img_filegroup") + ctx.CreateModuleInDirectory( + android.FileGroupFactory, + filepath.Dir(srcDestList[0]), + &struct { + Name *string + Srcs []string + }{ + Name: proptools.StringPtr(moduleName), + Srcs: []string{filepath.Base(srcDestList[1])}, + }, + ) + imgType := "vendor_boot" + if !buildingVendorBootImage(partitionVars) { + imgType = "boot" + } + return dtbImg{include: true, name: moduleName, imgType: imgType} + } + } + return dtbImg{include: false} +} diff --git a/fsgen/filesystem_creator.go b/fsgen/filesystem_creator.go index 6bfb097af..e8b0a4fb7 100644 --- a/fsgen/filesystem_creator.go +++ b/fsgen/filesystem_creator.go @@ -124,15 +124,17 @@ func (f *filesystemCreator) createInternalModules(ctx android.LoadHookContext) { } partitionVars := ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse + dtbImg := createDtbImgFilegroup(ctx) + if buildingBootImage(partitionVars) { - if createBootImage(ctx) { + if createBootImage(ctx, dtbImg) { f.properties.Boot_image = ":" + generatedModuleNameForPartition(ctx.Config(), "boot") } else { f.properties.Unsupported_partition_types = append(f.properties.Unsupported_partition_types, "boot") } } if buildingVendorBootImage(partitionVars) { - if createVendorBootImage(ctx) { + if createVendorBootImage(ctx, dtbImg) { f.properties.Vendor_boot_image = ":" + generatedModuleNameForPartition(ctx.Config(), "vendor_boot") } else { f.properties.Unsupported_partition_types = append(f.properties.Unsupported_partition_types, "vendor_boot") @@ -312,9 +314,11 @@ func (f *filesystemCreator) createPartition(ctx android.LoadHookContext, partiti return false } - if partitionType == "vendor" || partitionType == "product" { + if partitionType == "vendor" || partitionType == "product" || partitionType == "system" { fsProps.Linker_config.Gen_linker_config = proptools.BoolPtr(true) - fsProps.Linker_config.Linker_config_srcs = f.createLinkerConfigSourceFilegroups(ctx, partitionType) + if partitionType != "system" { + fsProps.Linker_config.Linker_config_srcs = f.createLinkerConfigSourceFilegroups(ctx, partitionType) + } } if android.InList(partitionType, dlkmPartitions) { @@ -546,42 +550,21 @@ func generateBaseProps(namePtr *string) *filesystemBaseProperty { } func generateFsProps(ctx android.EarlyModuleContext, partitionType string) (*filesystem.FilesystemProperties, bool) { - fsGenState := ctx.Config().Get(fsGenStateOnceKey).(*FsGenState) fsProps := &filesystem.FilesystemProperties{} partitionVars := ctx.Config().ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse - var boardAvbEnable bool - var boardAvbKeyPath string - var boardAvbAlgorithm string - var boardAvbRollbackIndex string + var avbInfo avbInfo var fsType string if strings.Contains(partitionType, "ramdisk") { fsType = "compressed_cpio" } else { specificPartitionVars := partitionVars.PartitionQualifiedVariables[partitionType] fsType = specificPartitionVars.BoardFileSystemType - boardAvbEnable = partitionVars.BoardAvbEnable - boardAvbKeyPath = specificPartitionVars.BoardAvbKeyPath - boardAvbAlgorithm = specificPartitionVars.BoardAvbAlgorithm - boardAvbRollbackIndex = specificPartitionVars.BoardAvbRollbackIndex - if boardAvbEnable { - if boardAvbKeyPath == "" { - boardAvbKeyPath = partitionVars.BoardAvbKeyPath - } - if boardAvbAlgorithm == "" { - boardAvbAlgorithm = partitionVars.BoardAvbAlgorithm - } - if boardAvbRollbackIndex == "" { - boardAvbRollbackIndex = partitionVars.BoardAvbRollbackIndex - } - } + avbInfo = getAvbInfo(ctx.Config(), partitionType) if fsType == "" { fsType = "ext4" //default } } - if boardAvbKeyPath != "" { - boardAvbKeyPath = ":" + fsGenState.avbKeyFilegroups[boardAvbKeyPath] - } fsProps.Type = proptools.StringPtr(fsType) if filesystem.GetFsTypeFromString(ctx, *fsProps.Type).IsUnknown() { @@ -594,15 +577,13 @@ func generateFsProps(ctx android.EarlyModuleContext, partitionType string) (*fil fsProps.Unchecked_module = proptools.BoolPtr(true) // BOARD_AVB_ENABLE - fsProps.Use_avb = proptools.BoolPtr(boardAvbEnable) + fsProps.Use_avb = avbInfo.avbEnable // BOARD_AVB_KEY_PATH - fsProps.Avb_private_key = proptools.StringPtr(boardAvbKeyPath) + fsProps.Avb_private_key = avbInfo.avbkeyFilegroup // BOARD_AVB_ALGORITHM - fsProps.Avb_algorithm = proptools.StringPtr(boardAvbAlgorithm) + fsProps.Avb_algorithm = avbInfo.avbAlgorithm // BOARD_AVB_SYSTEM_ROLLBACK_INDEX - if rollbackIndex, err := strconv.ParseInt(boardAvbRollbackIndex, 10, 64); err == nil { - fsProps.Rollback_index = proptools.Int64Ptr(rollbackIndex) - } + fsProps.Rollback_index = avbInfo.avbRollbackIndex fsProps.Partition_name = proptools.StringPtr(partitionType) @@ -629,6 +610,55 @@ func generateFsProps(ctx android.EarlyModuleContext, partitionType string) (*fil return fsProps, true } +type avbInfo struct { + avbEnable *bool + avbKeyPath *string + avbkeyFilegroup *string + avbAlgorithm *string + avbRollbackIndex *int64 + avbMode *string +} + +func getAvbInfo(config android.Config, partitionType string) avbInfo { + partitionVars := config.ProductVariables().PartitionVarsForSoongMigrationOnlyDoNotUse + specificPartitionVars := partitionVars.PartitionQualifiedVariables[partitionType] + var result avbInfo + boardAvbEnable := partitionVars.BoardAvbEnable + if boardAvbEnable { + result.avbEnable = proptools.BoolPtr(true) + if specificPartitionVars.BoardAvbKeyPath != "" { + result.avbKeyPath = proptools.StringPtr(specificPartitionVars.BoardAvbKeyPath) + } else if partitionVars.BoardAvbKeyPath != "" { + result.avbKeyPath = proptools.StringPtr(partitionVars.BoardAvbKeyPath) + } + if specificPartitionVars.BoardAvbAlgorithm != "" { + result.avbAlgorithm = proptools.StringPtr(specificPartitionVars.BoardAvbAlgorithm) + } else if partitionVars.BoardAvbAlgorithm != "" { + result.avbAlgorithm = proptools.StringPtr(partitionVars.BoardAvbAlgorithm) + } + if specificPartitionVars.BoardAvbRollbackIndex != "" { + parsed, err := strconv.ParseInt(specificPartitionVars.BoardAvbRollbackIndex, 10, 64) + if err != nil { + panic(fmt.Sprintf("Rollback index must be an int, got %s", specificPartitionVars.BoardAvbRollbackIndex)) + } + result.avbRollbackIndex = &parsed + } else if partitionVars.BoardAvbRollbackIndex != "" { + parsed, err := strconv.ParseInt(partitionVars.BoardAvbRollbackIndex, 10, 64) + if err != nil { + panic(fmt.Sprintf("Rollback index must be an int, got %s", partitionVars.BoardAvbRollbackIndex)) + } + result.avbRollbackIndex = &parsed + } + result.avbMode = proptools.StringPtr("make_legacy") + } + if result.avbKeyPath != nil { + fsGenState := config.Get(fsGenStateOnceKey).(*FsGenState) + filegroup := fsGenState.avbKeyFilegroups[*result.avbKeyPath] + result.avbkeyFilegroup = proptools.StringPtr(":" + filegroup) + } + return result +} + func (f *filesystemCreator) createFileListDiffTest(ctx android.ModuleContext, partitionType string) android.Path { partitionModuleName := generatedModuleNameForPartition(ctx.Config(), partitionType) systemImage := ctx.GetDirectDepWithTag(partitionModuleName, generatedFilesystemDepTag) diff --git a/fsgen/fsgen_mutators.go b/fsgen/fsgen_mutators.go index 12f99566e..9472a50ae 100644 --- a/fsgen/fsgen_mutators.go +++ b/fsgen/fsgen_mutators.go @@ -98,7 +98,6 @@ func createFsGenState(ctx android.LoadHookContext, generatedPrebuiltEtcModuleNam "com.android.apex.cts.shim.v1_prebuilt": defaultDepCandidateProps(ctx.Config()), "dex_bootjars": defaultDepCandidateProps(ctx.Config()), "framework_compatibility_matrix.device.xml": defaultDepCandidateProps(ctx.Config()), - "init.environ.rc-soong": defaultDepCandidateProps(ctx.Config()), "libcompiler_rt": defaultDepCandidateProps(ctx.Config()), "libdmabufheap": defaultDepCandidateProps(ctx.Config()), "libgsi": defaultDepCandidateProps(ctx.Config()), diff --git a/java/classpath_fragment.go b/java/classpath_fragment.go index fdccd3a84..88a8fb80a 100644 --- a/java/classpath_fragment.go +++ b/java/classpath_fragment.go @@ -105,6 +105,10 @@ func gatherPossibleApexModuleNamesAndStems(ctx android.ModuleContext, contents [ set := map[string]struct{}{} for _, name := range contents { dep := ctx.GetDirectDepWithTag(name, tag) + if dep == nil && ctx.Config().AllowMissingDependencies() { + // Ignore apex boot jars from dexpreopt if it does not exist, and missing deps are allowed. + continue + } set[ModuleStemForDeapexing(dep)] = struct{}{} if m, ok := dep.(ModuleWithStem); ok { set[m.Stem()] = struct{}{} diff --git a/java/droidstubs.go b/java/droidstubs.go index bc2652797..e955949af 100644 --- a/java/droidstubs.go +++ b/java/droidstubs.go @@ -769,15 +769,6 @@ func addMetalavaConfigFilesToCmd(cmd *android.RuleBuilderCommand, configFiles an // property is defined, apply transformations and only revert the flagged apis that are not // enabled via release configurations and are not specified in aconfig_declarations func generateRevertAnnotationArgs(ctx android.ModuleContext, cmd *android.RuleBuilderCommand, stubsType StubsType, aconfigFlagsPaths android.Paths) { - - if len(aconfigFlagsPaths) == 0 { - cmd.Flag("--revert-annotation android.annotation.FlaggedApi") - return - } - - releasedFlaggedApisFile := android.PathForModuleOut(ctx, fmt.Sprintf("released-flagged-apis-%s.txt", stubsType.String())) - revertAnnotationsFile := android.PathForModuleOut(ctx, fmt.Sprintf("revert-annotations-%s.txt", stubsType.String())) - var filterArgs string switch stubsType { // No flagged apis specific flags need to be passed to metalava when generating @@ -799,6 +790,15 @@ func generateRevertAnnotationArgs(ctx android.ModuleContext, cmd *android.RuleBu } } + if len(aconfigFlagsPaths) == 0 { + // This argument should not be added for "everything" stubs + cmd.Flag("--revert-annotation android.annotation.FlaggedApi") + return + } + + releasedFlaggedApisFile := android.PathForModuleOut(ctx, fmt.Sprintf("released-flagged-apis-%s.txt", stubsType.String())) + revertAnnotationsFile := android.PathForModuleOut(ctx, fmt.Sprintf("revert-annotations-%s.txt", stubsType.String())) + ctx.Build(pctx, android.BuildParams{ Rule: gatherReleasedFlaggedApisRule, Inputs: aconfigFlagsPaths, diff --git a/java/java.go b/java/java.go index 64ef782f5..260d33610 100644 --- a/java/java.go +++ b/java/java.go @@ -2311,14 +2311,17 @@ func (al *ApiLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) { case libTag: if provider, ok := android.OtherModuleProvider(ctx, dep, JavaInfoProvider); ok { classPaths = append(classPaths, provider.HeaderJars...) + al.aconfigProtoFiles = append(al.aconfigProtoFiles, provider.AconfigIntermediateCacheOutputPaths...) } case bootClasspathTag: if provider, ok := android.OtherModuleProvider(ctx, dep, JavaInfoProvider); ok { bootclassPaths = append(bootclassPaths, provider.HeaderJars...) + al.aconfigProtoFiles = append(al.aconfigProtoFiles, provider.AconfigIntermediateCacheOutputPaths...) } case staticLibTag: if provider, ok := android.OtherModuleProvider(ctx, dep, JavaInfoProvider); ok { staticLibs = append(staticLibs, provider.HeaderJars...) + al.aconfigProtoFiles = append(al.aconfigProtoFiles, provider.AconfigIntermediateCacheOutputPaths...) } case systemModulesTag: if sm, ok := android.OtherModuleProvider(ctx, dep, SystemModulesProvider); ok { diff --git a/java/testing.go b/java/testing.go index cb3245ba7..0ea4e6408 100644 --- a/java/testing.go +++ b/java/testing.go @@ -428,7 +428,7 @@ func gatherRequiredDepsForTest() string { "stub-annotations", "aconfig-annotations-lib", - "aconfig_storage_reader_java", + "aconfig_storage_stub", "unsupportedappusage", } diff --git a/rust/bindgen.go b/rust/bindgen.go index d59057994..898e7923b 100644 --- a/rust/bindgen.go +++ b/rust/bindgen.go @@ -300,6 +300,11 @@ func (b *bindgenDecorator) GenerateSource(ctx ModuleContext, deps PathDeps) andr // it cannot recognize. Turn off unknown warning flags warning. cflags = append(cflags, "-Wno-unknown-warning-option") + // Suppress warnings while testing a new compiler. + if ctx.Config().IsEnvTrue("LLVM_NEXT") { + cflags = append(cflags, "-Wno-everything") + } + outputFile := android.PathForModuleOut(ctx, b.BaseSourceProvider.getStem(ctx)+".rs") var cmd, cmdDesc string diff --git a/ui/status/log.go b/ui/status/log.go index 14df346de..7bfd39641 100644 --- a/ui/status/log.go +++ b/ui/status/log.go @@ -22,6 +22,8 @@ import ( "io/ioutil" "os" "strings" + "sync" + "time" "google.golang.org/protobuf/proto" @@ -31,7 +33,10 @@ import ( ) type verboseLog struct { - w io.WriteCloser + w *gzip.Writer + lock *sync.Mutex + data chan []string + stop chan bool } func NewVerboseLog(log logger.Logger, filename string) StatusOutput { @@ -47,9 +52,42 @@ func NewVerboseLog(log logger.Logger, filename string) StatusOutput { w := gzip.NewWriter(f) - return &verboseLog{ - w: w, + l := &verboseLog{ + w: w, + lock: &sync.Mutex{}, + data: make(chan []string), + stop: make(chan bool), } + l.startWriter() + return l +} + +func (v *verboseLog) startWriter() { + go func() { + tick := time.Tick(time.Second) + for { + select { + case <-v.stop: + close(v.data) + v.w.Close() + return + case <-tick: + v.w.Flush() + case dataList := <-v.data: + for _, data := range dataList { + fmt.Fprint(v.w, data) + } + } + } + }() +} + +func (v *verboseLog) stopWriter() { + v.stop <- true +} + +func (v *verboseLog) queueWrite(s ...string) { + v.data <- s } func (v *verboseLog) StartAction(action *Action, counts Counts) {} @@ -60,27 +98,27 @@ func (v *verboseLog) FinishAction(result ActionResult, counts Counts) { cmd = result.Description } - fmt.Fprintf(v.w, "[%d/%d] %s\n", counts.FinishedActions, counts.TotalActions, cmd) + v.queueWrite(fmt.Sprintf("[%d/%d] ", counts.FinishedActions, counts.TotalActions), cmd, "\n") if result.Error != nil { - fmt.Fprintf(v.w, "FAILED: %s\n", strings.Join(result.Outputs, " ")) + v.queueWrite("FAILED: ", strings.Join(result.Outputs, " "), "\n") } if result.Output != "" { - fmt.Fprintln(v.w, result.Output) + v.queueWrite(result.Output, "\n") } } func (v *verboseLog) Flush() { - v.w.Close() + v.stopWriter() } func (v *verboseLog) Message(level MsgLevel, message string) { - fmt.Fprintf(v.w, "%s%s\n", level.Prefix(), message) + v.queueWrite(level.Prefix(), message, "\n") } func (v *verboseLog) Write(p []byte) (int, error) { - fmt.Fprint(v.w, string(p)) + v.queueWrite(string(p)) return len(p), nil } |