diff --git a/apex/apex.go b/apex/apex.go index 276ac80d5..abab12b35 100644 --- a/apex/apex.go +++ b/apex/apex.go @@ -1104,6 +1104,7 @@ func apexStrictUpdatibilityLintMutator(mctx android.TopDownMutatorContext) { return } if apex, ok := mctx.Module().(*apexBundle); ok && apex.checkStrictUpdatabilityLinting() { + parents := []string{mctx.ModuleName()} mctx.WalkDeps(func(child, parent android.Module) bool { // b/208656169 Do not propagate strict updatability linting to libcore/ // These libs are available on the classpath during compilation @@ -1117,7 +1118,7 @@ func apexStrictUpdatibilityLintMutator(mctx android.TopDownMutatorContext) { return false } if lintable, ok := child.(java.LintDepSetsIntf); ok { - lintable.SetStrictUpdatabilityLinting(true) + lintable.SetStrictUpdatabilityLinting(parents) } // visit transitive deps return true diff --git a/java/lint.go b/java/lint.go index c79f6e7bb..7c06312ce 100644 --- a/java/lint.go +++ b/java/lint.go @@ -62,6 +62,10 @@ type LintProperties struct { // If true, baselining updatability lint checks (e.g. NewApi) is prohibited. Defaults to false. Strict_updatability_linting *bool + // The reverse dependency that had strict_updatability_linting set that caused this module to + // have it enabled, for use in error messages. + Strict_updatability_parents []string + // Treat the code in this module as test code for @VisibleForTesting enforcement. // This will be true by default for test module types, false otherwise. // If soong gets support for testonly, this flag should be replaced with that. @@ -117,8 +121,8 @@ type LintDepSetsIntf interface { LintDepSets() LintDepSets // Methods used to propagate strict_updatability_linting values. - GetStrictUpdatabilityLinting() bool - SetStrictUpdatabilityLinting(bool) + GetStrictUpdatabilityLinting(ctx android.BaseModuleContext) []string + SetStrictUpdatabilityLinting([]string) } type LintDepSets struct { @@ -213,12 +217,22 @@ func (l *linter) LintDepSets() LintDepSets { return l.outputs.depSets } -func (l *linter) GetStrictUpdatabilityLinting() bool { - return BoolDefault(l.properties.Lint.Strict_updatability_linting, false) +// GetStrictUpdatabilityLinting returns a list of names of modules +// that set strict_updatability_linting: true and affect the current module. +// Strict updatability linting should be enabled if the result is non-empty. +func (l *linter) GetStrictUpdatabilityLinting(ctx android.BaseModuleContext) []string { + result := l.properties.Lint.Strict_updatability_parents + if proptools.Bool(l.properties.Lint.Strict_updatability_linting) { + result = append(result, ctx.ModuleName()) + } + return result } -func (l *linter) SetStrictUpdatabilityLinting(strictLinting bool) { - l.properties.Lint.Strict_updatability_linting = &strictLinting +// SetStrictUpdatabilityLinting adds the given list of modules to this module's +// list of "strict updatable parent" modules. If then given list is non-empty, +// it causes strict updatability linting to be enabled on this module. +func (l *linter) SetStrictUpdatabilityLinting(parents []string) { + l.properties.Lint.Strict_updatability_parents = android.SortedUniqueStrings(append(l.properties.Lint.Strict_updatability_parents, parents...)) } var _ LintDepSetsIntf = (*linter)(nil) @@ -325,9 +339,10 @@ func (l *linter) writeLintProjectXML(ctx android.ModuleContext, rule *android.Ru cmd.FlagForEachArg("--error_check ", l.properties.Lint.Error_checks) cmd.FlagForEachArg("--fatal_check ", l.properties.Lint.Fatal_checks) - if l.GetStrictUpdatabilityLinting() { + if strict_mods := l.GetStrictUpdatabilityLinting(ctx); len(strict_mods) > 0 { // Verify the module does not baseline issues that endanger safe updatability. if l.properties.Lint.Baseline_filename != nil { + cmd.FlagWithArg("--strict_updatability_parents ", proptools.ShellEscape(strings.Join(strict_mods, ","))) cmd.FlagWithInput("--baseline ", android.PathForModuleSrc(ctx, *l.properties.Lint.Baseline_filename)) cmd.FlagForEachArg("--disallowed_issues ", updatabilityChecks) } @@ -733,11 +748,13 @@ func lintZip(ctx android.BuilderContext, paths android.Paths, outputPath android // Enforce the strict updatability linting to all applicable transitive dependencies. func enforceStrictUpdatabilityLintingMutator(ctx android.TopDownMutatorContext) { m := ctx.Module() - if d, ok := m.(LintDepSetsIntf); ok && d.GetStrictUpdatabilityLinting() { - ctx.VisitDirectDepsWithTag(staticLibTag, func(d android.Module) { - if a, ok := d.(LintDepSetsIntf); ok { - a.SetStrictUpdatabilityLinting(true) - } - }) + if d, ok := m.(LintDepSetsIntf); ok { + if strict_mods := d.GetStrictUpdatabilityLinting(ctx); len(strict_mods) > 0 { + ctx.VisitDirectDepsWithTag(staticLibTag, func(d android.Module) { + if a, ok := d.(LintDepSetsIntf); ok { + a.SetStrictUpdatabilityLinting(strict_mods) + } + }) + } } } diff --git a/java/lint_test.go b/java/lint_test.go index b51753f71..6b24daa5f 100644 --- a/java/lint_test.go +++ b/java/lint_test.go @@ -202,6 +202,71 @@ func TestJavaLintStrictUpdatabilityLinting(t *testing.T) { } } +func TestJavaLintStrictUpdatabilityLintingMultipleParents(t *testing.T) { + bp := ` + java_library { + name: "a", + srcs: ["a.java"], + static_libs: ["b"], + min_sdk_version: "29", + sdk_version: "current", + lint: { + strict_updatability_linting: true, + baseline_filename: "lint-baseline.xml", + }, + } + + java_library { + name: "b", + srcs: ["b.java"], + static_libs: ["c"], + min_sdk_version: "29", + sdk_version: "current", + lint: { + strict_updatability_linting: true, + }, + } + + java_library { + name: "d", + srcs: ["d.java"], + static_libs: ["c"], + min_sdk_version: "29", + sdk_version: "current", + lint: { + strict_updatability_linting: true, + }, + } + + java_library { + name: "c", + srcs: ["c.java"], + min_sdk_version: "29", + sdk_version: "current", + lint: { + baseline_filename: "lint-baseline.xml", + } + } + ` + fs := android.MockFS{ + "lint-baseline.xml": nil, + } + + result := android.GroupFixturePreparers(PrepareForTestWithJavaDefaultModules, fs.AddToFixture()). + RunTestWithBp(t, bp) + + c := result.ModuleForTests("c", "android_common") + sboxProto := android.RuleBuilderSboxProtoForTests(t, result.TestContext, c.Output("lint.sbox.textproto")) + if !strings.Contains(*sboxProto.Commands[0].Command, + "--baseline lint-baseline.xml --disallowed_issues NewApi") { + t.Error("did not restrict baselining NewApi") + } + if !strings.Contains(*sboxProto.Commands[0].Command, + "--strict_updatability_parents a,b,d") { + t.Errorf("Did not find correct strict_updatability_parents in command %q", *sboxProto.Commands[0].Command) + } +} + func TestJavaLintDatabaseSelectionFull(t *testing.T) { testCases := []struct { sdk_version string diff --git a/java/sdk_library.go b/java/sdk_library.go index 49e6727eb..570248ed3 100644 --- a/java/sdk_library.go +++ b/java/sdk_library.go @@ -2968,17 +2968,17 @@ func (module *SdkLibraryImport) LintDepSets() LintDepSets { } } -func (module *SdkLibraryImport) GetStrictUpdatabilityLinting() bool { +func (module *SdkLibraryImport) GetStrictUpdatabilityLinting(ctx android.BaseModuleContext) []string { if module.implLibraryModule == nil { - return false + return nil } else { - return module.implLibraryModule.GetStrictUpdatabilityLinting() + return module.implLibraryModule.GetStrictUpdatabilityLinting(ctx) } } -func (module *SdkLibraryImport) SetStrictUpdatabilityLinting(strictLinting bool) { +func (module *SdkLibraryImport) SetStrictUpdatabilityLinting(parents []string) { if module.implLibraryModule != nil { - module.implLibraryModule.SetStrictUpdatabilityLinting(strictLinting) + module.implLibraryModule.SetStrictUpdatabilityLinting(parents) } } diff --git a/scripts/lint_project_xml.py b/scripts/lint_project_xml.py index c40b07d38..cdc968aa9 100755 --- a/scripts/lint_project_xml.py +++ b/scripts/lint_project_xml.py @@ -77,6 +77,8 @@ def parse_args(): help='file containing merged manifest for the module and its dependencies.') parser.add_argument('--baseline', dest='baseline_path', help='file containing baseline lint issues.') + parser.add_argument('--strict_updatability_parents', + help='reverse dependencies that set strict_updatability_linting: true, for use in error messages') parser.add_argument('--library', dest='library', action='store_true', help='mark the module as a library.') parser.add_argument('--test', dest='test', action='store_true', @@ -161,8 +163,10 @@ def main(): baseline = minidom.parse(args.baseline_path) disallowed_issues = check_baseline_for_disallowed_issues(baseline, args.disallowed_issues) if disallowed_issues: - sys.exit('disallowed issues %s found in lint baseline file %s for module %s' - % (disallowed_issues, args.baseline_path, args.name)) + error_message = f'disallowed issues {disallowed_issues} found in lint baseline file {args.baseline_path} for module {args.name}' + if args.strict_updatability_parents: + error_message += f'. The parent modules that set strict_updatability_linting are {args.strict_updatability_parent}' + sys.exit(error_message) if args.project_out: with open(args.project_out, 'w') as f: