diff --git a/android/Android.bp b/android/Android.bp index d58370391..65332b2d8 100644 --- a/android/Android.bp +++ b/android/Android.bp @@ -49,6 +49,7 @@ bootstrap_go_package { "expand.go", "filegroup.go", "fixture.go", + "gen_notice.go", "hooks.go", "image.go", "license.go", @@ -106,6 +107,7 @@ bootstrap_go_package { "deptag_test.go", "expand_test.go", "fixture_test.go", + "gen_notice_test.go", "license_kind_test.go", "license_test.go", "licenses_test.go", diff --git a/android/gen_notice.go b/android/gen_notice.go new file mode 100644 index 000000000..e2b839f69 --- /dev/null +++ b/android/gen_notice.go @@ -0,0 +1,212 @@ +// Copyright 2020 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" + "strings" + + "github.com/google/blueprint/proptools" +) + +func init() { + RegisterGenNoticeBuildComponents(InitRegistrationContext) +} + +// Register the gen_notice module type. +func RegisterGenNoticeBuildComponents(ctx RegistrationContext) { + ctx.RegisterSingletonType("gen_notice_build_rules", GenNoticeBuildRulesFactory) + ctx.RegisterModuleType("gen_notice", GenNoticeFactory) +} + +type genNoticeBuildRules struct{} + +func (s *genNoticeBuildRules) GenerateBuildActions(ctx SingletonContext) { + ctx.VisitAllModules(func(m Module) { + gm, ok := m.(*genNoticeModule) + if !ok { + return + } + if len(gm.missing) > 0 { + missingReferencesRule(ctx, gm) + return + } + out := BuildNoticeTextOutputFromLicenseMetadata + if proptools.Bool(gm.properties.Xml) { + out = BuildNoticeXmlOutputFromLicenseMetadata + } else if proptools.Bool(gm.properties.Html) { + out = BuildNoticeHtmlOutputFromLicenseMetadata + } + defaultName := "" + if len(gm.properties.For) > 0 { + defaultName = gm.properties.For[0] + } + + modules := make([]Module, 0) + for _, name := range gm.properties.For { + mods := ctx.ModuleVariantsFromName(gm, name) + for _, mod := range mods { + if mod == nil { + continue + } + modules = append(modules, mod) + } + } + if ctx.Failed() { + return + } + out(ctx, gm.output, ctx.ModuleName(gm), + proptools.StringDefault(gm.properties.ArtifactName, defaultName), + []string{ + ctx.Config().OutDir() + "/", + ctx.Config().SoongOutDir() + "/", + }, modules...) + }) +} + +func GenNoticeBuildRulesFactory() Singleton { + return &genNoticeBuildRules{} +} + +type genNoticeProperties struct { + // For specifies the modules for which to generate a notice file. + For []string + // ArtifactName specifies the internal name to use for the notice file. + // It appears in the "used by:" list for targets whose entire name is stripped by --strip_prefix. + ArtifactName *string + // Stem specifies the base name of the output file. + Stem *string `android:"arch_variant"` + // Html indicates an html-format file is needed. The default is text. Can be Html or Xml but not both. + Html *bool + // Xml indicates an xml-format file is needed. The default is text. Can be Html or Xml but not both. + Xml *bool + // Gzipped indicates the output file must be compressed with gzip. Will append .gz to suffix if not there. + Gzipped *bool + // Suffix specifies the file extension to use. Defaults to .html for html, .xml for xml, or no extension for text. + Suffix *string + // Visibility specifies where this license can be used + Visibility []string +} + +type genNoticeModule struct { + ModuleBase + DefaultableModuleBase + + properties genNoticeProperties + + output OutputPath + missing []string +} + +func (m *genNoticeModule) DepsMutator(ctx BottomUpMutatorContext) { + if proptools.Bool(m.properties.Html) && proptools.Bool(m.properties.Xml) { + ctx.ModuleErrorf("can be html or xml but not both") + } + if !ctx.Config().AllowMissingDependencies() { + var missing []string + // Verify the modules for which to generate notices exist. + for _, otherMod := range m.properties.For { + if !ctx.OtherModuleExists(otherMod) { + missing = append(missing, otherMod) + } + } + if len(missing) == 1 { + ctx.PropertyErrorf("for", "no %q module exists", missing[0]) + } else if len(missing) > 1 { + ctx.PropertyErrorf("for", "modules \"%s\" do not exist", strings.Join(missing, "\", \"")) + } + } +} + +func (m *genNoticeModule) getStem() string { + stem := m.base().BaseModuleName() + if m.properties.Stem != nil { + stem = proptools.String(m.properties.Stem) + } + return stem +} + +func (m *genNoticeModule) getSuffix() string { + suffix := "" + if m.properties.Suffix == nil { + if proptools.Bool(m.properties.Html) { + suffix = ".html" + } else if proptools.Bool(m.properties.Xml) { + suffix = ".xml" + } + } else { + suffix = proptools.String(m.properties.Suffix) + } + if proptools.Bool(m.properties.Gzipped) && !strings.HasSuffix(suffix, ".gz") { + suffix += ".gz" + } + return suffix +} + +func (m *genNoticeModule) GenerateAndroidBuildActions(ctx ModuleContext) { + if ctx.Config().AllowMissingDependencies() { + // Verify the modules for which to generate notices exist. + for _, otherMod := range m.properties.For { + if !ctx.OtherModuleExists(otherMod) { + m.missing = append(m.missing, otherMod) + } + } + m.missing = append(m.missing, ctx.GetMissingDependencies()...) + m.missing = FirstUniqueStrings(m.missing) + } + out := m.getStem() + m.getSuffix() + m.output = PathForModuleOut(ctx, out).OutputPath +} + +func GenNoticeFactory() Module { + module := &genNoticeModule{} + + base := module.base() + module.AddProperties(&base.nameProperties, &module.properties) + + // The visibility property needs to be checked and parsed by the visibility module. + setPrimaryVisibilityProperty(module, "visibility", &module.properties.Visibility) + + initAndroidModuleBase(module) + InitDefaultableModule(module) + + return module +} + +var _ OutputFileProducer = (*genNoticeModule)(nil) + +// Implements OutputFileProducer +func (m *genNoticeModule) OutputFiles(tag string) (Paths, error) { + if tag == "" { + return Paths{m.output}, nil + } + return nil, fmt.Errorf("unrecognized tag %q", tag) +} + +// missingReferencesRule emits an ErrorRule for missing module references. +func missingReferencesRule(ctx BuilderContext, m *genNoticeModule) { + if len(m.missing) < 1 { + panic(fmt.Errorf("missing references rule requested with no missing references")) + } + + ctx.Build(pctx, BuildParams{ + Rule: ErrorRule, + Output: m.output, + Description: "notice for " + proptools.StringDefault(m.properties.ArtifactName, "container"), + Args: map[string]string{ + "error": m.Name() + " references missing module(s): " + strings.Join(m.missing, ", "), + }, + }) +} diff --git a/android/gen_notice_test.go b/android/gen_notice_test.go new file mode 100644 index 000000000..4ad2ecfa9 --- /dev/null +++ b/android/gen_notice_test.go @@ -0,0 +1,164 @@ +package android + +import ( + "testing" + + "github.com/google/blueprint" +) + +var genNoticeTests = []struct { + name string + fs MockFS + expectedErrors []string +}{ + { + name: "gen_notice must not accept licenses property", + fs: map[string][]byte{ + "top/Android.bp": []byte(` + gen_notice { + name: "top_license", + licenses: ["other_license"], + }`), + }, + expectedErrors: []string{ + `unrecognized property "licenses"`, + }, + }, + { + name: "bad gen_notice", + fs: map[string][]byte{ + "top/Android.bp": []byte(` + gen_notice { + name: "top_notice", + for: ["top_rule"], + }`), + "other/Android.bp": []byte(` + mock_genrule { + name: "other_rule", + dep: ["top_notice"], + }`), + }, + expectedErrors: []string{ + `module "top_notice": for: no "top_rule" module exists`, + }, + }, + { + name: "doubly bad gen_notice", + fs: map[string][]byte{ + "top/Android.bp": []byte(` + gen_notice { + name: "top_notice", + for: ["top_rule", "other_rule"], + }`), + }, + expectedErrors: []string{ + `module "top_notice": for: modules "top_rule", "other_rule" do not exist`, + }, + }, + { + name: "good gen_notice", + fs: map[string][]byte{ + "top/Android.bp": []byte(` + gen_notice { + name: "top_notice", + for: ["top_rule"], + } + + mock_genrule { + name: "top_rule", + dep: ["top_notice"], + }`), + "other/Android.bp": []byte(` + mock_genrule { + name: "other_rule", + dep: ["top_notice"], + }`), + }, + }, + { + name: "multiple license kinds", + fs: map[string][]byte{ + "top/Android.bp": []byte(` + gen_notice { + name: "top_notice", + for: ["top_rule"], + } + + gen_notice { + name: "top_html_notice", + html: true, + for: ["top_rule"], + } + + gen_notice { + name: "top_xml_notice", + xml: true, + for: ["top_notice"], + } + + mock_genrule { + name: "top_rule", + dep: [ + "top_notice", + "top_html_notice", + "top_xml_notice", + ], + }`), + "other/Android.bp": []byte(` + mock_genrule { + name: "other_rule", + dep: ["top_xml_notice"], + }`), + }, + }, +} + +func TestGenNotice(t *testing.T) { + for _, test := range genNoticeTests { + t.Run(test.name, func(t *testing.T) { + GroupFixturePreparers( + PrepareForTestWithGenNotice, + FixtureRegisterWithContext(func(ctx RegistrationContext) { + ctx.RegisterModuleType("mock_genrule", newMockGenruleModule) + }), + test.fs.AddToFixture(), + ). + ExtendWithErrorHandler(FixtureExpectsAllErrorsToMatchAPattern(test.expectedErrors)). + RunTest(t) + }) + } +} + +type mockGenruleProperties struct { + Dep []string +} + +type mockGenruleModule struct { + ModuleBase + DefaultableModuleBase + + properties mockGenruleProperties +} + +func newMockGenruleModule() Module { + m := &mockGenruleModule{} + m.AddProperties(&m.properties) + InitAndroidArchModule(m, HostAndDeviceSupported, MultilibCommon) + InitDefaultableModule(m) + return m +} + +type genruleDepTag struct { + blueprint.BaseDependencyTag +} + +func (j *mockGenruleModule) DepsMutator(ctx BottomUpMutatorContext) { + m, ok := ctx.Module().(Module) + if !ok { + return + } + ctx.AddDependency(m, genruleDepTag{}, j.properties.Dep...) +} + +func (p *mockGenruleModule) GenerateAndroidBuildActions(ModuleContext) { +} diff --git a/android/licenses.go b/android/licenses.go index bd14b26ca..81c557e72 100644 --- a/android/licenses.go +++ b/android/licenses.go @@ -303,6 +303,7 @@ func exemptFromRequiredApplicableLicensesProperty(module Module) bool { switch reflect.TypeOf(module).String() { case "*android.licenseModule": // is a license, doesn't need one case "*android.licenseKindModule": // is a license, doesn't need one + case "*android.genNoticeModule": // contains license texts as data case "*android.NamespaceModule": // just partitions things, doesn't add anything case "*android.soongConfigModuleTypeModule": // creates aliases for modules with licenses case "*android.soongConfigModuleTypeImport": // creates aliases for modules with licenses diff --git a/android/singleton.go b/android/singleton.go index 7ff96c9d5..7c6cf4fd6 100644 --- a/android/singleton.go +++ b/android/singleton.go @@ -29,6 +29,10 @@ type SingletonContext interface { ModuleType(module blueprint.Module) string BlueprintFile(module blueprint.Module) string + // ModuleVariantsFromName returns the list of module variants named `name` in the same namespace as `referer` enforcing visibility rules. + // Allows generating build actions for `referer` based on the metadata for `name` deferred until the singleton context. + ModuleVariantsFromName(referer Module, name string) []Module + // ModuleProvider returns the value, if any, for the provider for a module. If the value for the // provider was not set it returns the zero value of the type of the provider, which means the // return value can always be type-asserted to the type of the provider. The return value should @@ -251,3 +255,30 @@ func (s *singletonContextAdaptor) PrimaryModule(module Module) Module { func (s *singletonContextAdaptor) FinalModule(module Module) Module { return s.SingletonContext.FinalModule(module).(Module) } + +func (s *singletonContextAdaptor) ModuleVariantsFromName(referer Module, name string) []Module { + // get qualified module name for visibility enforcement + qualified := createQualifiedModuleName(s.ModuleName(referer), s.ModuleDir(referer)) + + modules := s.SingletonContext.ModuleVariantsFromName(referer, name) + result := make([]Module, 0, len(modules)) + for _, m := range modules { + if module, ok := m.(Module); ok { + // enforce visibility + depName := s.ModuleName(module) + depDir := s.ModuleDir(module) + depQualified := qualifiedModuleName{depDir, depName} + // Targets are always visible to other targets in their own package. + if depQualified.pkg != qualified.pkg { + rule := effectiveVisibilityRules(s.Config(), depQualified) + if !rule.matches(qualified) { + s.ModuleErrorf(referer, "module %q references %q which is not visible to this module\nYou may need to add %q to its visibility", + referer.Name(), depQualified, "//"+s.ModuleDir(referer)) + continue + } + } + result = append(result, module) + } + } + return result +} diff --git a/android/testing.go b/android/testing.go index ac02db9af..85bdca475 100644 --- a/android/testing.go +++ b/android/testing.go @@ -83,6 +83,8 @@ var PrepareForTestWithLicenses = GroupFixturePreparers( FixtureRegisterWithContext(registerLicenseMutators), ) +var PrepareForTestWithGenNotice = FixtureRegisterWithContext(RegisterGenNoticeBuildComponents) + func registerLicenseMutators(ctx RegistrationContext) { ctx.PreArchMutators(RegisterLicensesPackageMapper) ctx.PreArchMutators(RegisterLicensesPropertyGatherer) diff --git a/android/visibility.go b/android/visibility.go index 5d1be6b47..b20959944 100644 --- a/android/visibility.go +++ b/android/visibility.go @@ -234,7 +234,7 @@ func RegisterVisibilityRuleEnforcer(ctx RegisterMutatorsContext) { // Checks the per-module visibility rule lists before defaults expansion. func visibilityRuleChecker(ctx BottomUpMutatorContext) { - qualified := createQualifiedModuleName(ctx) + qualified := createQualifiedModuleName(ctx.ModuleName(), ctx.ModuleDir()) if m, ok := ctx.Module().(Module); ok { visibilityProperties := m.visibilityProperties() for _, p := range visibilityProperties { @@ -435,7 +435,7 @@ func visibilityRuleEnforcer(ctx TopDownMutatorContext) { return } - qualified := createQualifiedModuleName(ctx) + qualified := createQualifiedModuleName(ctx.ModuleName(), ctx.ModuleDir()) // Visit all the dependencies making sure that this module has access to them all. ctx.VisitDirectDeps(func(dep Module) { @@ -486,9 +486,7 @@ func effectiveVisibilityRules(config Config, qualified qualifiedModuleName) comp return rule } -func createQualifiedModuleName(ctx BaseModuleContext) qualifiedModuleName { - moduleName := ctx.ModuleName() - dir := ctx.ModuleDir() +func createQualifiedModuleName(moduleName, dir string) qualifiedModuleName { qualified := qualifiedModuleName{dir, moduleName} return qualified } diff --git a/android/visibility_test.go b/android/visibility_test.go index 714c92a71..a66f0b698 100644 --- a/android/visibility_test.go +++ b/android/visibility_test.go @@ -135,7 +135,49 @@ var visibilityTests = []struct { name: "libexample", visibility: ["//visibility:public"], } - + + mock_library { + name: "libsamepackage", + deps: ["libexample"], + } + + gen_notice { + name: "libexample-notice", + for: ["libexample"], + }`), + "top/nested/Android.bp": []byte(` + mock_library { + name: "libnested", + deps: ["libexample"], + } + + gen_notice { + name: "nested-notice", + for: ["libexample"], + }`), + "other/Android.bp": []byte(` + mock_library { + name: "libother", + deps: ["libexample"], + } + + gen_notice { + name: "other-notice", + for: ["libexample"], + }`), + }, + }, + { + // Verify that //visibility:private allows the module to be referenced from the current + // directory only. + name: "//visibility:private", + fs: MockFS{ + "top/Android.bp": []byte(` + mock_library { + name: "libexample", + visibility: ["//visibility:private"], + } + mock_library { name: "libsamepackage", deps: ["libexample"], @@ -151,18 +193,61 @@ var visibilityTests = []struct { deps: ["libexample"], }`), }, + expectedErrors: []string{ + `module "libnested" variant "android_common": depends on //top:libexample which is not` + + ` visible to this module`, + `module "libother" variant "android_common": depends on //top:libexample which is not` + + ` visible to this module`, + }, }, { // Verify that //visibility:private allows the module to be referenced from the current // directory only. - name: "//visibility:private", + name: "//visibility:private (notices)", fs: MockFS{ "top/Android.bp": []byte(` mock_library { name: "libexample", visibility: ["//visibility:private"], } - + + mock_library { + name: "libsamepackage", + deps: ["libexample"], + } + + gen_notice { + name: "libexample-notice", + for: ["libexample"], + }`), + "top/nested/Android.bp": []byte(` + gen_notice { + name: "nested-notice", + for: ["libexample"], + }`), + "other/Android.bp": []byte(` + gen_notice { + name: "other-notice", + for: ["libexample"], + }`), + }, + expectedErrors: []string{ + `module "nested-notice" references "//top:libexample" which is not visible to this` + + ` module\nYou may need to add "//top/nested" to its visibility`, + `module "other-notice" references "//top:libexample" which is not visible to this module\n` + + `You may need to add "//other" to its visibility`, + }, + }, + { + // Verify that :__pkg__ allows the module to be referenced from the current directory only. + name: ":__pkg__", + fs: MockFS{ + "top/Android.bp": []byte(` + mock_library { + name: "libexample", + visibility: [":__pkg__"], + } + mock_library { name: "libsamepackage", deps: ["libexample"], @@ -187,34 +272,32 @@ var visibilityTests = []struct { }, { // Verify that :__pkg__ allows the module to be referenced from the current directory only. - name: ":__pkg__", + name: ":__pkg__ (notices)", fs: MockFS{ "top/Android.bp": []byte(` mock_library { name: "libexample", visibility: [":__pkg__"], } - - mock_library { - name: "libsamepackage", - deps: ["libexample"], + + gen_notice { + name: "libexample-notice", + for: ["libexample"], }`), "top/nested/Android.bp": []byte(` - mock_library { - name: "libnested", - deps: ["libexample"], + gen_notice { + name: "nested-notice", + for: ["libexample"], }`), "other/Android.bp": []byte(` - mock_library { - name: "libother", - deps: ["libexample"], + gen_notice { + name: "other-notice", + for: ["libexample"], }`), }, expectedErrors: []string{ - `module "libnested" variant "android_common": depends on //top:libexample which is not` + - ` visible to this module`, - `module "libother" variant "android_common": depends on //top:libexample which is not` + - ` visible to this module`, + `module "nested-notice" references "//top:libexample" which is not visible to this module`, + `module "other-notice" references "//top:libexample" which is not visible to this module`, }, }, { @@ -227,7 +310,7 @@ var visibilityTests = []struct { name: "libexample", visibility: ["//top/nested"], } - + mock_library { name: "libsamepackage", deps: ["libexample"], @@ -255,6 +338,42 @@ var visibilityTests = []struct { ` visible to this module`, }, }, + { + // Verify that //top/nested allows the module to be referenced from the current directory and + // the top/nested directory only, not a subdirectory of top/nested and not peak directory. + name: "//top/nested (notices)", + fs: MockFS{ + "top/Android.bp": []byte(` + mock_library { + name: "libexample", + visibility: ["//top/nested"], + } + + gen_notice { + name: "libexample-notice", + for: ["libexample"], + }`), + "top/nested/Android.bp": []byte(` + gen_notice { + name: "nested-notice", + for: ["libexample"], + }`), + "top/nested/again/Android.bp": []byte(` + gen_notice { + name: "nestedagain-notice", + for: ["libexample"], + }`), + "peak/Android.bp": []byte(` + gen_notice { + name: "other-notice", + for: ["libexample"], + }`), + }, + expectedErrors: []string{ + `module "other-notice" references "//top:libexample" which is not visible to this module`, + `module "nestedagain-notice" references "//top:libexample" which is not visible to this module`, + }, + }, { // Verify that :__subpackages__ allows the module to be referenced from the current directory // and sub directories but nowhere else. @@ -265,7 +384,7 @@ var visibilityTests = []struct { name: "libexample", visibility: [":__subpackages__"], } - + mock_library { name: "libsamepackage", deps: ["libexample"], @@ -286,6 +405,36 @@ var visibilityTests = []struct { ` visible to this module`, }, }, + { + // Verify that :__subpackages__ allows the module to be referenced from the current directory + // and sub directories but nowhere else. + name: ":__subpackages__ (notices)", + fs: MockFS{ + "top/Android.bp": []byte(` + mock_library { + name: "libexample", + visibility: [":__subpackages__"], + } + + gen_notice { + name: "libexample-notice", + for: ["libexample"], + }`), + "top/nested/Android.bp": []byte(` + gen_notice { + name: "nested-notice", + for: ["libexample"], + }`), + "peak/other/Android.bp": []byte(` + gen_notice { + name: "other-notice", + for: ["libexample"], + }`), + }, + expectedErrors: []string{ + `module "other-notice" references "//top:libexample" which is not visible to this module`, + }, + }, { // Verify that //top/nested:__subpackages__ allows the module to be referenced from the current // directory and sub directories but nowhere else. @@ -296,7 +445,7 @@ var visibilityTests = []struct { name: "libexample", visibility: ["//top/nested:__subpackages__", "//other"], } - + mock_library { name: "libsamepackage", deps: ["libexample"], @@ -317,6 +466,36 @@ var visibilityTests = []struct { ` visible to this module`, }, }, + { + // Verify that //top/nested:__subpackages__ allows the module to be referenced from the current + // directory and sub directories but nowhere else. + name: "//top/nested:__subpackages__ (notices)", + fs: MockFS{ + "top/Android.bp": []byte(` + mock_library { + name: "libexample", + visibility: ["//top/nested:__subpackages__", "//other"], + } + + gen_notice { + name: "libexample-notice", + for: ["libexample"], + }`), + "top/nested/Android.bp": []byte(` + gen_notice { + name: "nested-notice", + for: ["libexample"], + }`), + "top/other/Android.bp": []byte(` + gen_notice { + name: "other-notice", + for: ["libexample"], + }`), + }, + expectedErrors: []string{ + `module "other-notice" references "//top:libexample" which is not visible to this module`, + }, + }, { // Verify that ["//top/nested", "//peak:__subpackages"] allows the module to be referenced from // the current directory, top/nested and peak and all its subpackages. @@ -327,7 +506,7 @@ var visibilityTests = []struct { name: "libexample", visibility: ["//top/nested", "//peak:__subpackages__"], } - + mock_library { name: "libsamepackage", deps: ["libexample"], @@ -344,6 +523,33 @@ var visibilityTests = []struct { }`), }, }, + { + // Verify that ["//top/nested", "//peak:__subpackages"] allows the module to be referenced from + // the current directory, top/nested and peak and all its subpackages. + name: `["//top/nested", "//peak:__subpackages__ (notices)"]`, + fs: MockFS{ + "top/Android.bp": []byte(` + mock_library { + name: "libexample", + visibility: ["//top/nested", "//peak:__subpackages__"], + } + + gen_notice { + name: "libexample-notice", + for: ["libexample"], + }`), + "top/nested/Android.bp": []byte(` + gen_notice { + name: "nested-notice", + for: ["libexample"], + }`), + "peak/other/Android.bp": []byte(` + gen_notice { + name: "other-notice", + for: ["libexample"], + }`), + }, + }, { // Verify that //vendor... cannot be used outside vendor apart from //vendor:__subpackages__ name: `//vendor`, @@ -353,7 +559,7 @@ var visibilityTests = []struct { name: "libexample", visibility: ["//vendor:__subpackages__"], } - + mock_library { name: "libsamepackage", visibility: ["//vendor/apps/AcmeSettings"], @@ -417,6 +623,45 @@ var visibilityTests = []struct { ` visible to this module`, }, }, + { + // Check that visibility is the union of the defaults modules. + name: "defaults union, basic (notices)", + fs: MockFS{ + "top/Android.bp": []byte(` + mock_defaults { + name: "libexample_defaults", + visibility: ["//other"], + } + mock_library { + name: "libexample", + visibility: ["//top/nested"], + defaults: ["libexample_defaults"], + } + + gen_notice { + name: "libexample-notice", + for: ["libexample"], + }`), + "top/nested/Android.bp": []byte(` + gen_notice { + name: "nested-notice", + for: ["libexample"], + }`), + "other/Android.bp": []byte(` + gen_notice { + name: "other-notice", + for: ["libexample"], + }`), + "outsider/Android.bp": []byte(` + gen_notice { + name: "outsider-notice", + for: ["libexample"], + }`), + }, + expectedErrors: []string{ + `module "outsider-notice" references "//top:libexample" which is not visible to this module`, + }, + }, { name: "defaults union, multiple defaults", fs: MockFS{ @@ -458,6 +703,47 @@ var visibilityTests = []struct { ` visible to this module`, }, }, + { + name: "defaults union, multiple defaults (notices)", + fs: MockFS{ + "top/Android.bp": []byte(` + mock_defaults { + name: "libexample_defaults_1", + visibility: ["//other"], + } + mock_defaults { + name: "libexample_defaults_2", + visibility: ["//top/nested"], + } + mock_library { + name: "libexample", + defaults: ["libexample_defaults_1", "libexample_defaults_2"], + } + + gen_notice { + name: "libexample-notice", + for: ["libexample"], + }`), + "top/nested/Android.bp": []byte(` + gen_notice { + name: "nested-notice", + for: ["libexample"], + }`), + "other/Android.bp": []byte(` + gen_notice { + name: "other-notice", + for: ["libexample"], + }`), + "outsider/Android.bp": []byte(` + gen_notice { + name: "outsider-notice", + for: ["libexample"], + }`), + }, + expectedErrors: []string{ + `module "outsider-notice" references "//top:libexample" which is not visible to this module`, + }, + }, { name: "//visibility:public mixed with other in defaults", fs: MockFS{ @@ -499,6 +785,29 @@ var visibilityTests = []struct { qualifiedModuleName{pkg: "top", name: "libexample"}: {"//visibility:public"}, }, }, + { + name: "//visibility:public overriding defaults (notices)", + fs: MockFS{ + "top/Android.bp": []byte(` + mock_defaults { + name: "libexample_defaults", + visibility: ["//namespace"], + } + mock_library { + name: "libexample", + visibility: ["//visibility:public"], + defaults: ["libexample_defaults"], + }`), + "outsider/Android.bp": []byte(` + gen_notice { + name: "outsider-notice", + for: ["libexample"], + }`), + }, + effectiveVisibility: map[qualifiedModuleName][]string{ + qualifiedModuleName{pkg: "top", name: "libexample"}: {"//visibility:public"}, + }, + }, { name: "//visibility:public mixed with other from different defaults 1", fs: MockFS{ @@ -522,6 +831,34 @@ var visibilityTests = []struct { }`), }, }, + { + name: "//visibility:public mixed with other from different defaults 1", + fs: MockFS{ + "top/Android.bp": []byte(` + mock_defaults { + name: "libexample_defaults_1", + visibility: ["//namespace"], + } + mock_defaults { + name: "libexample_defaults_2", + visibility: ["//visibility:public"], + } + mock_library { + name: "libexample", + defaults: ["libexample_defaults_1", "libexample_defaults_2"], + } + + gen_notice { + name: "libexample-notice", + for: ["libexample"], + }`), + "outsider/Android.bp": []byte(` + gen_notice { + name: "outsider-notice", + for: ["libexample"], + }`), + }, + }, { name: "//visibility:public mixed with other from different defaults 2", fs: MockFS{ @@ -545,6 +882,29 @@ var visibilityTests = []struct { }`), }, }, + { + name: "//visibility:public mixed with other from different defaults 2 (notices)", + fs: MockFS{ + "top/Android.bp": []byte(` + mock_defaults { + name: "libexample_defaults_1", + visibility: ["//visibility:public"], + } + mock_defaults { + name: "libexample_defaults_2", + visibility: ["//namespace"], + } + mock_library { + name: "libexample", + defaults: ["libexample_defaults_1", "libexample_defaults_2"], + }`), + "outsider/Android.bp": []byte(` + gen_notice { + name: "outsider-notice", + for: ["libexample"], + }`), + }, + }, { name: "//visibility:private in defaults", fs: MockFS{ @@ -579,6 +939,39 @@ var visibilityTests = []struct { ` visible to this module`, }, }, + { + name: "//visibility:private in defaults (notices)", + fs: MockFS{ + "top/Android.bp": []byte(` + mock_defaults { + name: "libexample_defaults", + visibility: ["//visibility:private"], + } + mock_library { + name: "libexample", + defaults: ["libexample_defaults"], + } + + gen_notice { + name: "libexample-notice", + for: ["libexample"], + }`), + "top/nested/Android.bp": []byte(` + gen_notice { + name: "nested-notice", + for: ["libexample"], + }`), + "other/Android.bp": []byte(` + gen_notice { + name: "other-notice", + for: ["libexample"], + }`), + }, + expectedErrors: []string{ + `module "nested-notice" references "//top:libexample" which is not visible to this module`, + `module "other-notice" references "//top:libexample" which is not visible to this module`, + }, + }, { name: "//visibility:private mixed with other in defaults", fs: MockFS{ @@ -705,6 +1098,27 @@ var visibilityTests = []struct { }`), }, }, + { + name: "//visibility:override discards //visibility:private (notices)", + fs: MockFS{ + "top/Android.bp": []byte(` + mock_defaults { + name: "libexample_defaults", + visibility: ["//visibility:private"], + } + mock_library { + name: "libexample", + // Make this visibility to //other but not //visibility:private + visibility: ["//visibility:override", "//other"], + defaults: ["libexample_defaults"], + }`), + "other/Android.bp": []byte(` + gen_notice { + name: "other-notice", + for: ["libexample"], + }`), + }, + }, { name: "//visibility:override discards //visibility:public", fs: MockFS{ @@ -734,6 +1148,35 @@ var visibilityTests = []struct { `module "libnamespace" variant "android_common": depends on //top:libexample which is not visible to this module\nYou may need to add "//namespace" to its visibility`, }, }, + { + name: "//visibility:override discards //visibility:public (notices)", + fs: MockFS{ + "top/Android.bp": []byte(` + mock_defaults { + name: "libexample_defaults", + visibility: ["//visibility:public"], + } + mock_library { + name: "libexample", + // Make this visibility to //other but not //visibility:public + visibility: ["//visibility:override", "//other"], + defaults: ["libexample_defaults"], + }`), + "other/Android.bp": []byte(` + gen_notice { + name: "other-notice", + for: ["libexample"], + }`), + "namespace/Android.bp": []byte(` + gen_notice { + name: "namespace-notice", + for: ["libexample"], + }`), + }, + expectedErrors: []string{ + `module "namespace-notice" references "//top:libexample" which is not visible to this module\nYou may need to add "//namespace" to its visibility`, + }, + }, { name: "//visibility:override discards defaults supplied rules", fs: MockFS{ @@ -763,6 +1206,35 @@ var visibilityTests = []struct { `module "libnamespace" variant "android_common": depends on //top:libexample which is not visible to this module\nYou may need to add "//namespace" to its visibility`, }, }, + { + name: "//visibility:override discards defaults supplied rules (notices)", + fs: MockFS{ + "top/Android.bp": []byte(` + mock_defaults { + name: "libexample_defaults", + visibility: ["//namespace"], + } + mock_library { + name: "libexample", + // Make this visibility to //other but not //namespace + visibility: ["//visibility:override", "//other"], + defaults: ["libexample_defaults"], + }`), + "other/Android.bp": []byte(` + gen_notice { + name: "other-notice", + for: ["libexample"], + }`), + "namespace/Android.bp": []byte(` + gen_notice { + name: "namespace-notice", + for: ["libexample"], + }`), + }, + expectedErrors: []string{ + `module "namespace-notice" references "//top:libexample" which is not visible to this module\nYou may need to add "//namespace" to its visibility`, + }, + }, { name: "//visibility:override can override //visibility:public with //visibility:private", fs: MockFS{ @@ -786,6 +1258,29 @@ var visibilityTests = []struct { `module "libnamespace" variant "android_common": depends on //top:libexample which is not visible to this module`, }, }, + { + name: "//visibility:override can override //visibility:public with //visibility:private (notices)", + fs: MockFS{ + "top/Android.bp": []byte(` + mock_defaults { + name: "libexample_defaults", + visibility: ["//visibility:public"], + } + mock_library { + name: "libexample", + visibility: ["//visibility:override", "//visibility:private"], + defaults: ["libexample_defaults"], + }`), + "namespace/Android.bp": []byte(` + gen_notice { + name: "namespace-notice", + for: ["libexample"], + }`), + }, + expectedErrors: []string{ + `module "namespace-notice" references "//top:libexample" which is not visible to this module`, + }, + }, { name: "//visibility:override can override //visibility:private with //visibility:public", fs: MockFS{ @@ -806,6 +1301,26 @@ var visibilityTests = []struct { }`), }, }, + { + name: "//visibility:override can override //visibility:private with //visibility:public (notices)", + fs: MockFS{ + "top/Android.bp": []byte(` + mock_defaults { + name: "libexample_defaults", + visibility: ["//visibility:private"], + } + mock_library { + name: "libexample", + visibility: ["//visibility:override", "//visibility:public"], + defaults: ["libexample_defaults"], + }`), + "namespace/Android.bp": []byte(` + gen_notice { + name: "namespace-notice", + for: ["libexample"], + }`), + }, + }, { name: "//visibility:private mixed with itself", fs: MockFS{ @@ -834,6 +1349,33 @@ var visibilityTests = []struct { ` visible to this module`, }, }, + { + name: "//visibility:private mixed with itself (notices)", + fs: MockFS{ + "top/Android.bp": []byte(` + mock_defaults { + name: "libexample_defaults_1", + visibility: ["//visibility:private"], + } + mock_defaults { + name: "libexample_defaults_2", + visibility: ["//visibility:private"], + } + mock_library { + name: "libexample", + visibility: ["//visibility:private"], + defaults: ["libexample_defaults_1", "libexample_defaults_2"], + }`), + "outsider/Android.bp": []byte(` + gen_notice { + name: "outsider-notice", + for: ["libexample"], + }`), + }, + expectedErrors: []string{ + `module "outsider-notice" references "//top:libexample" which is not visible to this module`, + }, + }, // Defaults module's defaults_visibility tests { @@ -902,6 +1444,28 @@ var visibilityTests = []struct { ` visible to this module`, }, }, + { + // This test relies on the default visibility being legacy_public. + name: "package default_visibility property used when no visibility specified (notices)", + fs: MockFS{ + "top/Android.bp": []byte(` + package { + default_visibility: ["//visibility:private"], + } + + mock_library { + name: "libexample", + }`), + "outsider/Android.bp": []byte(` + gen_notice { + name: "outsider-notice", + for: ["libexample"], + }`), + }, + expectedErrors: []string{ + `module "outsider-notice" references "//top:libexample" which is not visible to this module`, + }, + }, { name: "package default_visibility public does not override visibility private", fs: MockFS{ @@ -925,6 +1489,28 @@ var visibilityTests = []struct { ` visible to this module`, }, }, + { + name: "package default_visibility public does not override visibility private (notices)", + fs: MockFS{ + "top/Android.bp": []byte(` + package { + default_visibility: ["//visibility:public"], + } + + mock_library { + name: "libexample", + visibility: ["//visibility:private"], + }`), + "outsider/Android.bp": []byte(` + gen_notice { + name: "outsider-notice", + for: ["libexample"], + }`), + }, + expectedErrors: []string{ + `module "outsider-notice" references "//top:libexample" which is not visible to this module`, + }, + }, { name: "package default_visibility private does not override visibility public", fs: MockFS{ @@ -944,6 +1530,25 @@ var visibilityTests = []struct { }`), }, }, + { + name: "package default_visibility private does not override visibility public (notices)", + fs: MockFS{ + "top/Android.bp": []byte(` + package { + default_visibility: ["//visibility:private"], + } + + mock_library { + name: "libexample", + visibility: ["//visibility:public"], + }`), + "outsider/Android.bp": []byte(` + gen_notice { + name: "outsider-notice", + for: ["libexample"], + }`), + }, + }, { name: "package default_visibility :__subpackages__", fs: MockFS{ @@ -971,6 +1576,32 @@ var visibilityTests = []struct { ` visible to this module`, }, }, + { + name: "package default_visibility :__subpackages__ (notices)", + fs: MockFS{ + "top/Android.bp": []byte(` + package { + default_visibility: [":__subpackages__"], + } + + mock_library { + name: "libexample", + }`), + "top/nested/Android.bp": []byte(` + gen_notice { + name: "nested-notice", + for: ["libexample"], + }`), + "outsider/Android.bp": []byte(` + gen_notice { + name: "outsider-notice", + for: ["libexample"], + }`), + }, + expectedErrors: []string{ + `module "outsider-notice" references "//top:libexample" which is not visible to this module`, + }, + }, { name: "package default_visibility inherited to subpackages", fs: MockFS{ @@ -981,7 +1612,7 @@ var visibilityTests = []struct { mock_library { name: "libexample", - visibility: [":__subpackages__"], + visibility: [":__subpackages__"], }`), "top/nested/Android.bp": []byte(` mock_library { @@ -999,6 +1630,38 @@ var visibilityTests = []struct { ` visible to this module`, }, }, + { + name: "package default_visibility inherited to subpackages (notices)", + fs: MockFS{ + "top/Android.bp": []byte(` + package { + default_visibility: ["//outsider"], + } + + mock_library { + name: "libexample", + visibility: [":__subpackages__"], + }`), + "top/nested/Android.bp": []byte(` + mock_library { + name: "libnested", + deps: ["libexample"], + } + + gen_notice { + name: "nested-notice", + for: ["libexample"], + }`), + "outsider/Android.bp": []byte(` + gen_notice { + name: "outsider-notice", + for: ["libexample", "libnested"], + }`), + }, + expectedErrors: []string{ + `module "outsider-notice" references "//top:libexample" which is not visible to this module`, + }, + }, { name: "package default_visibility inherited to subpackages", fs: MockFS{ @@ -1029,6 +1692,41 @@ var visibilityTests = []struct { ` not visible to this module`, }, }, + { + name: "package default_visibility inherited to subpackages (notices)", + fs: MockFS{ + "top/Android.bp": []byte(` + package { + default_visibility: ["//visibility:private"], + }`), + "top/nested/Android.bp": []byte(` + package { + default_visibility: ["//outsider"], + } + + mock_library { + name: "libnested", + }`), + "top/other/Android.bp": []byte(` + mock_library { + name: "libother", + } + + gen_notice { + name: "other-notice", + for: ["libother"], + }`), + "outsider/Android.bp": []byte(` + gen_notice { + name: "outsider-notice", + for: ["libother", "libnested"], + }`), + }, + expectedErrors: []string{ + `module "outsider-notice" references "//top/other:libother" which is not visible to this` + + ` module\nYou may need to add "//outsider" to its visibility`, + }, + }, { name: "verify that prebuilt dependencies are ignored for visibility reasons (not preferred)", fs: MockFS{ @@ -1051,6 +1749,28 @@ var visibilityTests = []struct { }`), }, }, + { + name: "verify that prebuilt dependencies are ignored for visibility reasons (not preferred) (notices)", + fs: MockFS{ + "prebuilts/Android.bp": []byte(` + prebuilt { + name: "module", + visibility: ["//top/other"], + }`), + "top/sources/source_file": nil, + "top/sources/Android.bp": []byte(` + source { + name: "module", + visibility: ["//top/other"], + }`), + "top/other/source_file": nil, + "top/other/Android.bp": []byte(` + gen_notice { + name: "other-notice", + for: ["module"], + }`), + }, + }, { name: "verify that prebuilt dependencies are ignored for visibility reasons (preferred)", fs: MockFS{ @@ -1074,6 +1794,29 @@ var visibilityTests = []struct { }`), }, }, + { + name: "verify that prebuilt dependencies are ignored for visibility reasons (preferred) (notices)", + fs: MockFS{ + "prebuilts/Android.bp": []byte(` + prebuilt { + name: "module", + visibility: ["//top/other"], + prefer: true, + }`), + "top/sources/source_file": nil, + "top/sources/Android.bp": []byte(` + source { + name: "module", + visibility: ["//top/other"], + }`), + "top/other/source_file": nil, + "top/other/Android.bp": []byte(` + gen_notice { + name: "other-notice", + for: ["module"], + }`), + }, + }, { name: "ensure visibility properties are checked for correctness", fs: MockFS{ @@ -1137,6 +1880,30 @@ var visibilityTests = []struct { }`), }, }, + { + name: "automatic visibility inheritance enabled (notices)", + fs: MockFS{ + "top/Android.bp": []byte(` + mock_parent { + name: "parent", + visibility: ["//top/nested"], + child: { + name: "libchild", + visibility: ["//top/other"], + }, + }`), + "top/nested/Android.bp": []byte(` + gen_notice { + name: "nested-notice", + for: ["libchild"], + }`), + "top/other/Android.bp": []byte(` + gen_notice { + name: "other-notice", + for: ["libchild"], + }`), + }, + }, } func TestVisibility(t *testing.T) { @@ -1147,6 +1914,7 @@ func TestVisibility(t *testing.T) { // registration order. PrepareForTestWithArchMutator, PrepareForTestWithDefaults, + PrepareForTestWithGenNotice, PrepareForTestWithOverrides, PrepareForTestWithPackageModule, PrepareForTestWithPrebuilts, diff --git a/java/app.go b/java/app.go index 41419ba78..94e6fb950 100755 --- a/java/app.go +++ b/java/app.go @@ -589,16 +589,6 @@ func (a *AndroidApp) generateAndroidBuildActions(ctx android.ModuleContext) { a.classLoaderContexts = a.usesLibrary.classLoaderContextForUsesLibDeps(ctx) - var noticeAssetPath android.WritablePath - if Bool(a.appProperties.Embed_notices) || ctx.Config().IsEnvTrue("ALWAYS_EMBED_NOTICES") { - // The rule to create the notice file can't be generated yet, as the final output path - // for the apk isn't known yet. Add the path where the notice file will be generated to the - // aapt rules now before calling aaptBuildActions, the rule to create the notice file will - // be generated later. - noticeAssetPath = android.PathForModuleOut(ctx, "NOTICE", "NOTICE.html.gz") - a.aapt.noticeFile = android.OptionalPathForPath(noticeAssetPath) - } - // Process all building blocks, from AAPT to certificates. a.aaptBuildActions(ctx) @@ -673,8 +663,7 @@ func (a *AndroidApp) generateAndroidBuildActions(ctx android.ModuleContext) { a.extraOutputFiles = append(a.extraOutputFiles, v4SignatureFile) } - if a.aapt.noticeFile.Valid() { - // Generating the notice file rule has to be here after a.outputFile is known. + if Bool(a.appProperties.Embed_notices) || ctx.Config().IsEnvTrue("ALWAYS_EMBED_NOTICES") { noticeFile := android.PathForModuleOut(ctx, "NOTICE.html.gz") android.BuildNoticeHtmlOutputFromLicenseMetadata( ctx, noticeFile, "", "", @@ -683,11 +672,13 @@ func (a *AndroidApp) generateAndroidBuildActions(ctx android.ModuleContext) { android.PathForModuleInstall(ctx).String() + "/", a.outputFile.String(), }) + noticeAssetPath := android.PathForModuleOut(ctx, "NOTICE", "NOTICE.html.gz") builder := android.NewRuleBuilder(pctx, ctx) builder.Command().Text("cp"). Input(noticeFile). Output(noticeAssetPath) builder.Build("notice_dir", "Building notice dir") + a.aapt.noticeFile = android.OptionalPathForPath(noticeAssetPath) } for _, split := range a.aapt.splits {