Some mainline modules provide an `sdk` and one or more set of module_exports. When the snapshots are unpacked into separate prebuilts directories (one per snapshot) then any dependencies from a member of one snapshot to a member of another may require the latter to have additional visibility rules. Previously, these rules had to be added to each source module. This change allows additional visibility rules to be specified on the sdk/module_exports that are added to all the prebuilts in its snapshot. Bug: 155921753 Bug: 168301990 Test: m nothing Change-Id: Ia3eacb5749981f04770ae9872a8013e43c5c6ef1
1319 lines
34 KiB
Go
1319 lines
34 KiB
Go
package android
|
|
|
|
import (
|
|
"reflect"
|
|
"testing"
|
|
|
|
"github.com/google/blueprint"
|
|
)
|
|
|
|
var visibilityTests = []struct {
|
|
name string
|
|
fs map[string][]byte
|
|
expectedErrors []string
|
|
effectiveVisibility map[qualifiedModuleName][]string
|
|
}{
|
|
{
|
|
name: "invalid visibility: empty list",
|
|
fs: map[string][]byte{
|
|
"top/Blueprints": []byte(`
|
|
mock_library {
|
|
name: "libexample",
|
|
visibility: [],
|
|
}`),
|
|
},
|
|
expectedErrors: []string{`visibility: must contain at least one visibility rule`},
|
|
},
|
|
{
|
|
name: "invalid visibility: empty rule",
|
|
fs: map[string][]byte{
|
|
"top/Blueprints": []byte(`
|
|
mock_library {
|
|
name: "libexample",
|
|
visibility: [""],
|
|
}`),
|
|
},
|
|
expectedErrors: []string{`visibility: invalid visibility pattern ""`},
|
|
},
|
|
{
|
|
name: "invalid visibility: unqualified",
|
|
fs: map[string][]byte{
|
|
"top/Blueprints": []byte(`
|
|
mock_library {
|
|
name: "libexample",
|
|
visibility: ["target"],
|
|
}`),
|
|
},
|
|
expectedErrors: []string{`visibility: invalid visibility pattern "target"`},
|
|
},
|
|
{
|
|
name: "invalid visibility: empty namespace",
|
|
fs: map[string][]byte{
|
|
"top/Blueprints": []byte(`
|
|
mock_library {
|
|
name: "libexample",
|
|
visibility: ["//"],
|
|
}`),
|
|
},
|
|
expectedErrors: []string{`visibility: invalid visibility pattern "//"`},
|
|
},
|
|
{
|
|
name: "invalid visibility: empty module",
|
|
fs: map[string][]byte{
|
|
"top/Blueprints": []byte(`
|
|
mock_library {
|
|
name: "libexample",
|
|
visibility: [":"],
|
|
}`),
|
|
},
|
|
expectedErrors: []string{`visibility: invalid visibility pattern ":"`},
|
|
},
|
|
{
|
|
name: "invalid visibility: empty namespace and module",
|
|
fs: map[string][]byte{
|
|
"top/Blueprints": []byte(`
|
|
mock_library {
|
|
name: "libexample",
|
|
visibility: ["//:"],
|
|
}`),
|
|
},
|
|
expectedErrors: []string{`visibility: invalid visibility pattern "//:"`},
|
|
},
|
|
{
|
|
name: "//visibility:unknown",
|
|
fs: map[string][]byte{
|
|
"top/Blueprints": []byte(`
|
|
mock_library {
|
|
name: "libexample",
|
|
visibility: ["//visibility:unknown"],
|
|
}`),
|
|
},
|
|
expectedErrors: []string{`unrecognized visibility rule "//visibility:unknown"`},
|
|
},
|
|
{
|
|
name: "//visibility:xxx mixed",
|
|
fs: map[string][]byte{
|
|
"top/Blueprints": []byte(`
|
|
mock_library {
|
|
name: "libexample",
|
|
visibility: ["//visibility:public", "//namespace"],
|
|
}
|
|
|
|
mock_library {
|
|
name: "libother",
|
|
visibility: ["//visibility:private", "//namespace"],
|
|
}`),
|
|
},
|
|
expectedErrors: []string{
|
|
`module "libother": visibility: cannot mix "//visibility:private"` +
|
|
` with any other visibility rules`,
|
|
`module "libexample": visibility: cannot mix "//visibility:public"` +
|
|
` with any other visibility rules`,
|
|
},
|
|
},
|
|
{
|
|
name: "//visibility:legacy_public",
|
|
fs: map[string][]byte{
|
|
"top/Blueprints": []byte(`
|
|
mock_library {
|
|
name: "libexample",
|
|
visibility: ["//visibility:legacy_public"],
|
|
}`),
|
|
},
|
|
expectedErrors: []string{
|
|
`module "libexample": visibility: //visibility:legacy_public must` +
|
|
` not be used`,
|
|
},
|
|
},
|
|
{
|
|
// Verify that //visibility:public will allow the module to be referenced from anywhere, e.g.
|
|
// the current directory, a nested directory and a directory in a separate tree.
|
|
name: "//visibility:public",
|
|
fs: map[string][]byte{
|
|
"top/Blueprints": []byte(`
|
|
mock_library {
|
|
name: "libexample",
|
|
visibility: ["//visibility:public"],
|
|
}
|
|
|
|
mock_library {
|
|
name: "libsamepackage",
|
|
deps: ["libexample"],
|
|
}`),
|
|
"top/nested/Blueprints": []byte(`
|
|
mock_library {
|
|
name: "libnested",
|
|
deps: ["libexample"],
|
|
}`),
|
|
"other/Blueprints": []byte(`
|
|
mock_library {
|
|
name: "libother",
|
|
deps: ["libexample"],
|
|
}`),
|
|
},
|
|
},
|
|
{
|
|
// Verify that //visibility:private allows the module to be referenced from the current
|
|
// directory only.
|
|
name: "//visibility:private",
|
|
fs: map[string][]byte{
|
|
"top/Blueprints": []byte(`
|
|
mock_library {
|
|
name: "libexample",
|
|
visibility: ["//visibility:private"],
|
|
}
|
|
|
|
mock_library {
|
|
name: "libsamepackage",
|
|
deps: ["libexample"],
|
|
}`),
|
|
"top/nested/Blueprints": []byte(`
|
|
mock_library {
|
|
name: "libnested",
|
|
deps: ["libexample"],
|
|
}`),
|
|
"other/Blueprints": []byte(`
|
|
mock_library {
|
|
name: "libother",
|
|
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 :__pkg__ allows the module to be referenced from the current directory only.
|
|
name: ":__pkg__",
|
|
fs: map[string][]byte{
|
|
"top/Blueprints": []byte(`
|
|
mock_library {
|
|
name: "libexample",
|
|
visibility: [":__pkg__"],
|
|
}
|
|
|
|
mock_library {
|
|
name: "libsamepackage",
|
|
deps: ["libexample"],
|
|
}`),
|
|
"top/nested/Blueprints": []byte(`
|
|
mock_library {
|
|
name: "libnested",
|
|
deps: ["libexample"],
|
|
}`),
|
|
"other/Blueprints": []byte(`
|
|
mock_library {
|
|
name: "libother",
|
|
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 //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",
|
|
fs: map[string][]byte{
|
|
"top/Blueprints": []byte(`
|
|
mock_library {
|
|
name: "libexample",
|
|
visibility: ["//top/nested"],
|
|
}
|
|
|
|
mock_library {
|
|
name: "libsamepackage",
|
|
deps: ["libexample"],
|
|
}`),
|
|
"top/nested/Blueprints": []byte(`
|
|
mock_library {
|
|
name: "libnested",
|
|
deps: ["libexample"],
|
|
}`),
|
|
"top/nested/again/Blueprints": []byte(`
|
|
mock_library {
|
|
name: "libnestedagain",
|
|
deps: ["libexample"],
|
|
}`),
|
|
"peak/Blueprints": []byte(`
|
|
mock_library {
|
|
name: "libother",
|
|
deps: ["libexample"],
|
|
}`),
|
|
},
|
|
expectedErrors: []string{
|
|
`module "libother" variant "android_common": depends on //top:libexample which is not` +
|
|
` visible to this module`,
|
|
`module "libnestedagain" variant "android_common": depends on //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.
|
|
name: ":__subpackages__",
|
|
fs: map[string][]byte{
|
|
"top/Blueprints": []byte(`
|
|
mock_library {
|
|
name: "libexample",
|
|
visibility: [":__subpackages__"],
|
|
}
|
|
|
|
mock_library {
|
|
name: "libsamepackage",
|
|
deps: ["libexample"],
|
|
}`),
|
|
"top/nested/Blueprints": []byte(`
|
|
mock_library {
|
|
name: "libnested",
|
|
deps: ["libexample"],
|
|
}`),
|
|
"peak/other/Blueprints": []byte(`
|
|
mock_library {
|
|
name: "libother",
|
|
deps: ["libexample"],
|
|
}`),
|
|
},
|
|
expectedErrors: []string{
|
|
`module "libother" variant "android_common": depends on //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.
|
|
name: "//top/nested:__subpackages__",
|
|
fs: map[string][]byte{
|
|
"top/Blueprints": []byte(`
|
|
mock_library {
|
|
name: "libexample",
|
|
visibility: ["//top/nested:__subpackages__", "//other"],
|
|
}
|
|
|
|
mock_library {
|
|
name: "libsamepackage",
|
|
deps: ["libexample"],
|
|
}`),
|
|
"top/nested/Blueprints": []byte(`
|
|
mock_library {
|
|
name: "libnested",
|
|
deps: ["libexample"],
|
|
}`),
|
|
"top/other/Blueprints": []byte(`
|
|
mock_library {
|
|
name: "libother",
|
|
deps: ["libexample"],
|
|
}`),
|
|
},
|
|
expectedErrors: []string{
|
|
`module "libother" variant "android_common": depends on //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.
|
|
name: `["//top/nested", "//peak:__subpackages__"]`,
|
|
fs: map[string][]byte{
|
|
"top/Blueprints": []byte(`
|
|
mock_library {
|
|
name: "libexample",
|
|
visibility: ["//top/nested", "//peak:__subpackages__"],
|
|
}
|
|
|
|
mock_library {
|
|
name: "libsamepackage",
|
|
deps: ["libexample"],
|
|
}`),
|
|
"top/nested/Blueprints": []byte(`
|
|
mock_library {
|
|
name: "libnested",
|
|
deps: ["libexample"],
|
|
}`),
|
|
"peak/other/Blueprints": []byte(`
|
|
mock_library {
|
|
name: "libother",
|
|
deps: ["libexample"],
|
|
}`),
|
|
},
|
|
},
|
|
{
|
|
// Verify that //vendor... cannot be used outside vendor apart from //vendor:__subpackages__
|
|
name: `//vendor`,
|
|
fs: map[string][]byte{
|
|
"top/Blueprints": []byte(`
|
|
mock_library {
|
|
name: "libexample",
|
|
visibility: ["//vendor:__subpackages__"],
|
|
}
|
|
|
|
mock_library {
|
|
name: "libsamepackage",
|
|
visibility: ["//vendor/apps/AcmeSettings"],
|
|
}`),
|
|
"vendor/Blueprints": []byte(`
|
|
mock_library {
|
|
name: "libvendorexample",
|
|
deps: ["libexample"],
|
|
visibility: ["//vendor/nested"],
|
|
}`),
|
|
"vendor/nested/Blueprints": []byte(`
|
|
mock_library {
|
|
name: "libvendornested",
|
|
deps: ["libexample", "libvendorexample"],
|
|
}`),
|
|
},
|
|
expectedErrors: []string{
|
|
`module "libsamepackage": visibility: "//vendor/apps/AcmeSettings"` +
|
|
` is not allowed. Packages outside //vendor cannot make themselves visible to specific` +
|
|
` targets within //vendor, they can only use //vendor:__subpackages__.`,
|
|
},
|
|
},
|
|
|
|
// Defaults propagation tests
|
|
{
|
|
// Check that visibility is the union of the defaults modules.
|
|
name: "defaults union, basic",
|
|
fs: map[string][]byte{
|
|
"top/Blueprints": []byte(`
|
|
mock_defaults {
|
|
name: "libexample_defaults",
|
|
visibility: ["//other"],
|
|
}
|
|
mock_library {
|
|
name: "libexample",
|
|
visibility: ["//top/nested"],
|
|
defaults: ["libexample_defaults"],
|
|
}
|
|
mock_library {
|
|
name: "libsamepackage",
|
|
deps: ["libexample"],
|
|
}`),
|
|
"top/nested/Blueprints": []byte(`
|
|
mock_library {
|
|
name: "libnested",
|
|
deps: ["libexample"],
|
|
}`),
|
|
"other/Blueprints": []byte(`
|
|
mock_library {
|
|
name: "libother",
|
|
deps: ["libexample"],
|
|
}`),
|
|
"outsider/Blueprints": []byte(`
|
|
mock_library {
|
|
name: "liboutsider",
|
|
deps: ["libexample"],
|
|
}`),
|
|
},
|
|
expectedErrors: []string{
|
|
`module "liboutsider" variant "android_common": depends on //top:libexample which is not` +
|
|
` visible to this module`,
|
|
},
|
|
},
|
|
{
|
|
name: "defaults union, multiple defaults",
|
|
fs: map[string][]byte{
|
|
"top/Blueprints": []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"],
|
|
}
|
|
mock_library {
|
|
name: "libsamepackage",
|
|
deps: ["libexample"],
|
|
}`),
|
|
"top/nested/Blueprints": []byte(`
|
|
mock_library {
|
|
name: "libnested",
|
|
deps: ["libexample"],
|
|
}`),
|
|
"other/Blueprints": []byte(`
|
|
mock_library {
|
|
name: "libother",
|
|
deps: ["libexample"],
|
|
}`),
|
|
"outsider/Blueprints": []byte(`
|
|
mock_library {
|
|
name: "liboutsider",
|
|
deps: ["libexample"],
|
|
}`),
|
|
},
|
|
expectedErrors: []string{
|
|
`module "liboutsider" variant "android_common": depends on //top:libexample which is not` +
|
|
` visible to this module`,
|
|
},
|
|
},
|
|
{
|
|
name: "//visibility:public mixed with other in defaults",
|
|
fs: map[string][]byte{
|
|
"top/Blueprints": []byte(`
|
|
mock_defaults {
|
|
name: "libexample_defaults",
|
|
visibility: ["//visibility:public", "//namespace"],
|
|
}
|
|
mock_library {
|
|
name: "libexample",
|
|
defaults: ["libexample_defaults"],
|
|
}`),
|
|
},
|
|
expectedErrors: []string{
|
|
`module "libexample_defaults": visibility: cannot mix "//visibility:public"` +
|
|
` with any other visibility rules`,
|
|
},
|
|
},
|
|
{
|
|
name: "//visibility:public overriding defaults",
|
|
fs: map[string][]byte{
|
|
"top/Blueprints": []byte(`
|
|
mock_defaults {
|
|
name: "libexample_defaults",
|
|
visibility: ["//namespace"],
|
|
}
|
|
mock_library {
|
|
name: "libexample",
|
|
visibility: ["//visibility:public"],
|
|
defaults: ["libexample_defaults"],
|
|
}`),
|
|
"outsider/Blueprints": []byte(`
|
|
mock_library {
|
|
name: "liboutsider",
|
|
deps: ["libexample"],
|
|
}`),
|
|
},
|
|
effectiveVisibility: map[qualifiedModuleName][]string{
|
|
qualifiedModuleName{pkg: "top", name: "libexample"}: {"//visibility:public"},
|
|
},
|
|
},
|
|
{
|
|
name: "//visibility:public mixed with other from different defaults 1",
|
|
fs: map[string][]byte{
|
|
"top/Blueprints": []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"],
|
|
}`),
|
|
"outsider/Blueprints": []byte(`
|
|
mock_library {
|
|
name: "liboutsider",
|
|
deps: ["libexample"],
|
|
}`),
|
|
},
|
|
},
|
|
{
|
|
name: "//visibility:public mixed with other from different defaults 2",
|
|
fs: map[string][]byte{
|
|
"top/Blueprints": []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/Blueprints": []byte(`
|
|
mock_library {
|
|
name: "liboutsider",
|
|
deps: ["libexample"],
|
|
}`),
|
|
},
|
|
},
|
|
{
|
|
name: "//visibility:private in defaults",
|
|
fs: map[string][]byte{
|
|
"top/Blueprints": []byte(`
|
|
mock_defaults {
|
|
name: "libexample_defaults",
|
|
visibility: ["//visibility:private"],
|
|
}
|
|
mock_library {
|
|
name: "libexample",
|
|
defaults: ["libexample_defaults"],
|
|
}
|
|
mock_library {
|
|
name: "libsamepackage",
|
|
deps: ["libexample"],
|
|
}`),
|
|
"top/nested/Blueprints": []byte(`
|
|
mock_library {
|
|
name: "libnested",
|
|
deps: ["libexample"],
|
|
}`),
|
|
"other/Blueprints": []byte(`
|
|
mock_library {
|
|
name: "libother",
|
|
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`,
|
|
},
|
|
},
|
|
{
|
|
name: "//visibility:private mixed with other in defaults",
|
|
fs: map[string][]byte{
|
|
"top/Blueprints": []byte(`
|
|
mock_defaults {
|
|
name: "libexample_defaults",
|
|
visibility: ["//visibility:private", "//namespace"],
|
|
}
|
|
mock_library {
|
|
name: "libexample",
|
|
defaults: ["libexample_defaults"],
|
|
}`),
|
|
},
|
|
expectedErrors: []string{
|
|
`module "libexample_defaults": visibility: cannot mix "//visibility:private"` +
|
|
` with any other visibility rules`,
|
|
},
|
|
},
|
|
{
|
|
name: "//visibility:private overriding defaults",
|
|
fs: map[string][]byte{
|
|
"top/Blueprints": []byte(`
|
|
mock_defaults {
|
|
name: "libexample_defaults",
|
|
visibility: ["//namespace"],
|
|
}
|
|
mock_library {
|
|
name: "libexample",
|
|
visibility: ["//visibility:private"],
|
|
defaults: ["libexample_defaults"],
|
|
}`),
|
|
},
|
|
expectedErrors: []string{
|
|
`module "libexample": visibility: cannot mix "//visibility:private"` +
|
|
` with any other visibility rules`,
|
|
},
|
|
},
|
|
{
|
|
name: "//visibility:private in defaults overridden",
|
|
fs: map[string][]byte{
|
|
"top/Blueprints": []byte(`
|
|
mock_defaults {
|
|
name: "libexample_defaults",
|
|
visibility: ["//visibility:private"],
|
|
}
|
|
mock_library {
|
|
name: "libexample",
|
|
visibility: ["//namespace"],
|
|
defaults: ["libexample_defaults"],
|
|
}`),
|
|
},
|
|
expectedErrors: []string{
|
|
`module "libexample": visibility: cannot mix "//visibility:private"` +
|
|
` with any other visibility rules`,
|
|
},
|
|
},
|
|
{
|
|
name: "//visibility:private override //visibility:public",
|
|
fs: map[string][]byte{
|
|
"top/Blueprints": []byte(`
|
|
mock_defaults {
|
|
name: "libexample_defaults",
|
|
visibility: ["//visibility:public"],
|
|
}
|
|
mock_library {
|
|
name: "libexample",
|
|
visibility: ["//visibility:private"],
|
|
defaults: ["libexample_defaults"],
|
|
}`),
|
|
},
|
|
expectedErrors: []string{
|
|
`module "libexample": visibility: cannot mix "//visibility:private" with any other visibility rules`,
|
|
},
|
|
},
|
|
{
|
|
name: "//visibility:public override //visibility:private",
|
|
fs: map[string][]byte{
|
|
"top/Blueprints": []byte(`
|
|
mock_defaults {
|
|
name: "libexample_defaults",
|
|
visibility: ["//visibility:private"],
|
|
}
|
|
mock_library {
|
|
name: "libexample",
|
|
visibility: ["//visibility:public"],
|
|
defaults: ["libexample_defaults"],
|
|
}`),
|
|
},
|
|
expectedErrors: []string{
|
|
`module "libexample": visibility: cannot mix "//visibility:private" with any other visibility rules`,
|
|
},
|
|
},
|
|
{
|
|
name: "//visibility:override must be first in the list",
|
|
fs: map[string][]byte{
|
|
"top/Blueprints": []byte(`
|
|
mock_library {
|
|
name: "libexample",
|
|
visibility: ["//other", "//visibility:override", "//namespace"],
|
|
}`),
|
|
},
|
|
expectedErrors: []string{
|
|
`module "libexample": visibility: "//visibility:override" may only be used at the start of the visibility rules`,
|
|
},
|
|
},
|
|
{
|
|
name: "//visibility:override discards //visibility:private",
|
|
fs: map[string][]byte{
|
|
"top/Blueprints": []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/Blueprints": []byte(`
|
|
mock_library {
|
|
name: "libother",
|
|
deps: ["libexample"],
|
|
}`),
|
|
},
|
|
},
|
|
{
|
|
name: "//visibility:override discards //visibility:public",
|
|
fs: map[string][]byte{
|
|
"top/Blueprints": []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/Blueprints": []byte(`
|
|
mock_library {
|
|
name: "libother",
|
|
deps: ["libexample"],
|
|
}`),
|
|
"namespace/Blueprints": []byte(`
|
|
mock_library {
|
|
name: "libnamespace",
|
|
deps: ["libexample"],
|
|
}`),
|
|
},
|
|
expectedErrors: []string{
|
|
`module "libnamespace" variant "android_common": depends on //top:libexample which is not visible to this module`,
|
|
},
|
|
},
|
|
{
|
|
name: "//visibility:override discards defaults supplied rules",
|
|
fs: map[string][]byte{
|
|
"top/Blueprints": []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/Blueprints": []byte(`
|
|
mock_library {
|
|
name: "libother",
|
|
deps: ["libexample"],
|
|
}`),
|
|
"namespace/Blueprints": []byte(`
|
|
mock_library {
|
|
name: "libnamespace",
|
|
deps: ["libexample"],
|
|
}`),
|
|
},
|
|
expectedErrors: []string{
|
|
`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",
|
|
fs: map[string][]byte{
|
|
"top/Blueprints": []byte(`
|
|
mock_defaults {
|
|
name: "libexample_defaults",
|
|
visibility: ["//visibility:public"],
|
|
}
|
|
mock_library {
|
|
name: "libexample",
|
|
visibility: ["//visibility:override", "//visibility:private"],
|
|
defaults: ["libexample_defaults"],
|
|
}`),
|
|
"namespace/Blueprints": []byte(`
|
|
mock_library {
|
|
name: "libnamespace",
|
|
deps: ["libexample"],
|
|
}`),
|
|
},
|
|
expectedErrors: []string{
|
|
`module "libnamespace" variant "android_common": depends on //top:libexample which is not visible to this module`,
|
|
},
|
|
},
|
|
{
|
|
name: "//visibility:override can override //visibility:private with //visibility:public",
|
|
fs: map[string][]byte{
|
|
"top/Blueprints": []byte(`
|
|
mock_defaults {
|
|
name: "libexample_defaults",
|
|
visibility: ["//visibility:private"],
|
|
}
|
|
mock_library {
|
|
name: "libexample",
|
|
visibility: ["//visibility:override", "//visibility:public"],
|
|
defaults: ["libexample_defaults"],
|
|
}`),
|
|
"namespace/Blueprints": []byte(`
|
|
mock_library {
|
|
name: "libnamespace",
|
|
deps: ["libexample"],
|
|
}`),
|
|
},
|
|
},
|
|
{
|
|
name: "//visibility:private mixed with itself",
|
|
fs: map[string][]byte{
|
|
"top/Blueprints": []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/Blueprints": []byte(`
|
|
mock_library {
|
|
name: "liboutsider",
|
|
deps: ["libexample"],
|
|
}`),
|
|
},
|
|
expectedErrors: []string{
|
|
`module "liboutsider" variant "android_common": depends on //top:libexample which is not` +
|
|
` visible to this module`,
|
|
},
|
|
},
|
|
|
|
// Defaults module's defaults_visibility tests
|
|
{
|
|
name: "defaults_visibility invalid",
|
|
fs: map[string][]byte{
|
|
"top/Blueprints": []byte(`
|
|
mock_defaults {
|
|
name: "top_defaults",
|
|
defaults_visibility: ["//visibility:invalid"],
|
|
}`),
|
|
},
|
|
expectedErrors: []string{
|
|
`defaults_visibility: unrecognized visibility rule "//visibility:invalid"`,
|
|
},
|
|
},
|
|
{
|
|
name: "defaults_visibility overrides package default",
|
|
fs: map[string][]byte{
|
|
"top/Blueprints": []byte(`
|
|
package {
|
|
default_visibility: ["//visibility:private"],
|
|
}
|
|
mock_defaults {
|
|
name: "top_defaults",
|
|
defaults_visibility: ["//visibility:public"],
|
|
}`),
|
|
"outsider/Blueprints": []byte(`
|
|
mock_library {
|
|
name: "liboutsider",
|
|
defaults: ["top_defaults"],
|
|
}`),
|
|
},
|
|
},
|
|
|
|
// Package default_visibility tests
|
|
{
|
|
name: "package default_visibility property is checked",
|
|
fs: map[string][]byte{
|
|
"top/Blueprints": []byte(`
|
|
package {
|
|
default_visibility: ["//visibility:invalid"],
|
|
}`),
|
|
},
|
|
expectedErrors: []string{`default_visibility: unrecognized visibility rule "//visibility:invalid"`},
|
|
},
|
|
{
|
|
// This test relies on the default visibility being legacy_public.
|
|
name: "package default_visibility property used when no visibility specified",
|
|
fs: map[string][]byte{
|
|
"top/Blueprints": []byte(`
|
|
package {
|
|
default_visibility: ["//visibility:private"],
|
|
}
|
|
|
|
mock_library {
|
|
name: "libexample",
|
|
}`),
|
|
"outsider/Blueprints": []byte(`
|
|
mock_library {
|
|
name: "liboutsider",
|
|
deps: ["libexample"],
|
|
}`),
|
|
},
|
|
expectedErrors: []string{
|
|
`module "liboutsider" variant "android_common": depends on //top:libexample which is not` +
|
|
` visible to this module`,
|
|
},
|
|
},
|
|
{
|
|
name: "package default_visibility public does not override visibility private",
|
|
fs: map[string][]byte{
|
|
"top/Blueprints": []byte(`
|
|
package {
|
|
default_visibility: ["//visibility:public"],
|
|
}
|
|
|
|
mock_library {
|
|
name: "libexample",
|
|
visibility: ["//visibility:private"],
|
|
}`),
|
|
"outsider/Blueprints": []byte(`
|
|
mock_library {
|
|
name: "liboutsider",
|
|
deps: ["libexample"],
|
|
}`),
|
|
},
|
|
expectedErrors: []string{
|
|
`module "liboutsider" variant "android_common": depends on //top:libexample which is not` +
|
|
` visible to this module`,
|
|
},
|
|
},
|
|
{
|
|
name: "package default_visibility private does not override visibility public",
|
|
fs: map[string][]byte{
|
|
"top/Blueprints": []byte(`
|
|
package {
|
|
default_visibility: ["//visibility:private"],
|
|
}
|
|
|
|
mock_library {
|
|
name: "libexample",
|
|
visibility: ["//visibility:public"],
|
|
}`),
|
|
"outsider/Blueprints": []byte(`
|
|
mock_library {
|
|
name: "liboutsider",
|
|
deps: ["libexample"],
|
|
}`),
|
|
},
|
|
},
|
|
{
|
|
name: "package default_visibility :__subpackages__",
|
|
fs: map[string][]byte{
|
|
"top/Blueprints": []byte(`
|
|
package {
|
|
default_visibility: [":__subpackages__"],
|
|
}
|
|
|
|
mock_library {
|
|
name: "libexample",
|
|
}`),
|
|
"top/nested/Blueprints": []byte(`
|
|
mock_library {
|
|
name: "libnested",
|
|
deps: ["libexample"],
|
|
}`),
|
|
"outsider/Blueprints": []byte(`
|
|
mock_library {
|
|
name: "liboutsider",
|
|
deps: ["libexample"],
|
|
}`),
|
|
},
|
|
expectedErrors: []string{
|
|
`module "liboutsider" variant "android_common": depends on //top:libexample which is not` +
|
|
` visible to this module`,
|
|
},
|
|
},
|
|
{
|
|
name: "package default_visibility inherited to subpackages",
|
|
fs: map[string][]byte{
|
|
"top/Blueprints": []byte(`
|
|
package {
|
|
default_visibility: ["//outsider"],
|
|
}
|
|
|
|
mock_library {
|
|
name: "libexample",
|
|
visibility: [":__subpackages__"],
|
|
}`),
|
|
"top/nested/Blueprints": []byte(`
|
|
mock_library {
|
|
name: "libnested",
|
|
deps: ["libexample"],
|
|
}`),
|
|
"outsider/Blueprints": []byte(`
|
|
mock_library {
|
|
name: "liboutsider",
|
|
deps: ["libexample", "libnested"],
|
|
}`),
|
|
},
|
|
expectedErrors: []string{
|
|
`module "liboutsider" variant "android_common": depends on //top:libexample which is not` +
|
|
` visible to this module`,
|
|
},
|
|
},
|
|
{
|
|
name: "package default_visibility inherited to subpackages",
|
|
fs: map[string][]byte{
|
|
"top/Blueprints": []byte(`
|
|
package {
|
|
default_visibility: ["//visibility:private"],
|
|
}`),
|
|
"top/nested/Blueprints": []byte(`
|
|
package {
|
|
default_visibility: ["//outsider"],
|
|
}
|
|
|
|
mock_library {
|
|
name: "libnested",
|
|
}`),
|
|
"top/other/Blueprints": []byte(`
|
|
mock_library {
|
|
name: "libother",
|
|
}`),
|
|
"outsider/Blueprints": []byte(`
|
|
mock_library {
|
|
name: "liboutsider",
|
|
deps: ["libother", "libnested"],
|
|
}`),
|
|
},
|
|
expectedErrors: []string{
|
|
`module "liboutsider" variant "android_common": depends on //top/other:libother which is` +
|
|
` not visible to this module`,
|
|
},
|
|
},
|
|
{
|
|
name: "verify that prebuilt dependencies are ignored for visibility reasons (not preferred)",
|
|
fs: map[string][]byte{
|
|
"prebuilts/Blueprints": []byte(`
|
|
prebuilt {
|
|
name: "module",
|
|
visibility: ["//top/other"],
|
|
}`),
|
|
"top/sources/source_file": nil,
|
|
"top/sources/Blueprints": []byte(`
|
|
source {
|
|
name: "module",
|
|
visibility: ["//top/other"],
|
|
}`),
|
|
"top/other/source_file": nil,
|
|
"top/other/Blueprints": []byte(`
|
|
source {
|
|
name: "other",
|
|
deps: [":module"],
|
|
}`),
|
|
},
|
|
},
|
|
{
|
|
name: "verify that prebuilt dependencies are ignored for visibility reasons (preferred)",
|
|
fs: map[string][]byte{
|
|
"prebuilts/Blueprints": []byte(`
|
|
prebuilt {
|
|
name: "module",
|
|
visibility: ["//top/other"],
|
|
prefer: true,
|
|
}`),
|
|
"top/sources/source_file": nil,
|
|
"top/sources/Blueprints": []byte(`
|
|
source {
|
|
name: "module",
|
|
visibility: ["//top/other"],
|
|
}`),
|
|
"top/other/source_file": nil,
|
|
"top/other/Blueprints": []byte(`
|
|
source {
|
|
name: "other",
|
|
deps: [":module"],
|
|
}`),
|
|
},
|
|
},
|
|
{
|
|
name: "ensure visibility properties are checked for correctness",
|
|
fs: map[string][]byte{
|
|
"top/Blueprints": []byte(`
|
|
mock_parent {
|
|
name: "parent",
|
|
visibility: ["//top/nested"],
|
|
child: {
|
|
name: "libchild",
|
|
visibility: ["top/other"],
|
|
},
|
|
}`),
|
|
},
|
|
expectedErrors: []string{
|
|
`module "parent": child.visibility: invalid visibility pattern "top/other"`,
|
|
},
|
|
},
|
|
{
|
|
name: "invalid visibility added to child detected during gather phase",
|
|
fs: map[string][]byte{
|
|
"top/Blueprints": []byte(`
|
|
mock_parent {
|
|
name: "parent",
|
|
visibility: ["//top/nested"],
|
|
child: {
|
|
name: "libchild",
|
|
invalid_visibility: ["top/other"],
|
|
},
|
|
}`),
|
|
},
|
|
expectedErrors: []string{
|
|
// That this error is reported against the child not the parent shows it was
|
|
// not being detected in the parent which is correct as invalid_visibility is
|
|
// purposely not added to the list of visibility properties to check, and was
|
|
// in fact detected in the child in the gather phase. Contrast this error message
|
|
// with the preceding one.
|
|
`module "libchild" \(created by module "parent"\): visibility: invalid visibility pattern "top/other"`,
|
|
},
|
|
},
|
|
{
|
|
name: "automatic visibility inheritance enabled",
|
|
fs: map[string][]byte{
|
|
"top/Blueprints": []byte(`
|
|
mock_parent {
|
|
name: "parent",
|
|
visibility: ["//top/nested"],
|
|
child: {
|
|
name: "libchild",
|
|
visibility: ["//top/other"],
|
|
},
|
|
}`),
|
|
"top/nested/Blueprints": []byte(`
|
|
mock_library {
|
|
name: "libnested",
|
|
deps: ["libchild"],
|
|
}`),
|
|
"top/other/Blueprints": []byte(`
|
|
mock_library {
|
|
name: "libother",
|
|
deps: ["libchild"],
|
|
}`),
|
|
},
|
|
},
|
|
}
|
|
|
|
func TestVisibility(t *testing.T) {
|
|
for _, test := range visibilityTests {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
ctx, errs := testVisibility(buildDir, test.fs)
|
|
|
|
CheckErrorsAgainstExpectations(t, errs, test.expectedErrors)
|
|
|
|
if test.effectiveVisibility != nil {
|
|
checkEffectiveVisibility(t, ctx, test.effectiveVisibility)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func checkEffectiveVisibility(t *testing.T, ctx *TestContext, effectiveVisibility map[qualifiedModuleName][]string) {
|
|
for moduleName, expectedRules := range effectiveVisibility {
|
|
rule := effectiveVisibilityRules(ctx.config, moduleName)
|
|
stringRules := rule.Strings()
|
|
if !reflect.DeepEqual(expectedRules, stringRules) {
|
|
t.Errorf("effective rules mismatch: expected %q, found %q", expectedRules, stringRules)
|
|
}
|
|
}
|
|
}
|
|
|
|
func testVisibility(buildDir string, fs map[string][]byte) (*TestContext, []error) {
|
|
|
|
// Create a new config per test as visibility information is stored in the config.
|
|
config := TestArchConfig(buildDir, nil, "", fs)
|
|
|
|
ctx := NewTestArchContext()
|
|
ctx.RegisterModuleType("mock_library", newMockLibraryModule)
|
|
ctx.RegisterModuleType("mock_parent", newMockParentFactory)
|
|
ctx.RegisterModuleType("mock_defaults", defaultsFactory)
|
|
|
|
// Order of the following method calls is significant.
|
|
RegisterPackageBuildComponents(ctx)
|
|
registerTestPrebuiltBuildComponents(ctx)
|
|
ctx.PreArchMutators(RegisterVisibilityRuleChecker)
|
|
ctx.PreArchMutators(RegisterDefaultsPreArchMutators)
|
|
ctx.PreArchMutators(RegisterVisibilityRuleGatherer)
|
|
ctx.PostDepsMutators(RegisterVisibilityRuleEnforcer)
|
|
ctx.Register(config)
|
|
|
|
_, errs := ctx.ParseBlueprintsFiles(".")
|
|
if len(errs) > 0 {
|
|
return ctx, errs
|
|
}
|
|
|
|
_, errs = ctx.PrepareBuildActions(config)
|
|
return ctx, errs
|
|
}
|
|
|
|
type mockLibraryProperties struct {
|
|
Deps []string
|
|
}
|
|
|
|
type mockLibraryModule struct {
|
|
ModuleBase
|
|
DefaultableModuleBase
|
|
properties mockLibraryProperties
|
|
}
|
|
|
|
func newMockLibraryModule() Module {
|
|
m := &mockLibraryModule{}
|
|
m.AddProperties(&m.properties)
|
|
InitAndroidArchModule(m, HostAndDeviceSupported, MultilibCommon)
|
|
InitDefaultableModule(m)
|
|
return m
|
|
}
|
|
|
|
type dependencyTag struct {
|
|
blueprint.BaseDependencyTag
|
|
name string
|
|
}
|
|
|
|
func (j *mockLibraryModule) DepsMutator(ctx BottomUpMutatorContext) {
|
|
ctx.AddVariationDependencies(nil, dependencyTag{name: "mockdeps"}, j.properties.Deps...)
|
|
}
|
|
|
|
func (p *mockLibraryModule) GenerateAndroidBuildActions(ModuleContext) {
|
|
}
|
|
|
|
type mockDefaults struct {
|
|
ModuleBase
|
|
DefaultsModuleBase
|
|
}
|
|
|
|
func defaultsFactory() Module {
|
|
m := &mockDefaults{}
|
|
InitDefaultsModule(m)
|
|
return m
|
|
}
|
|
|
|
type mockParentProperties struct {
|
|
Child struct {
|
|
Name *string
|
|
|
|
// Visibility to pass to the child module.
|
|
Visibility []string
|
|
|
|
// Purposely not validated visibility to pass to the child.
|
|
Invalid_visibility []string
|
|
}
|
|
}
|
|
|
|
type mockParent struct {
|
|
ModuleBase
|
|
DefaultableModuleBase
|
|
properties mockParentProperties
|
|
}
|
|
|
|
func (p *mockParent) GenerateAndroidBuildActions(ModuleContext) {
|
|
}
|
|
|
|
func newMockParentFactory() Module {
|
|
m := &mockParent{}
|
|
m.AddProperties(&m.properties)
|
|
InitAndroidArchModule(m, HostAndDeviceSupported, MultilibCommon)
|
|
InitDefaultableModule(m)
|
|
AddVisibilityProperty(m, "child.visibility", &m.properties.Child.Visibility)
|
|
|
|
m.SetDefaultableHook(func(ctx DefaultableHookContext) {
|
|
visibility := m.properties.Child.Visibility
|
|
visibility = append(visibility, m.properties.Child.Invalid_visibility...)
|
|
ctx.CreateModule(newMockLibraryModule, &struct {
|
|
Name *string
|
|
Visibility []string
|
|
}{m.properties.Child.Name, visibility})
|
|
})
|
|
return m
|
|
}
|
|
|
|
func testVisibilityRuleSet(t *testing.T, rules, extra, expected []string) {
|
|
t.Helper()
|
|
set := &visibilityRuleSet{rules}
|
|
err := set.Widen(extra)
|
|
if err != nil {
|
|
t.Error(err)
|
|
return
|
|
}
|
|
actual := set.Strings()
|
|
if !reflect.DeepEqual(actual, expected) {
|
|
t.Errorf("mismatching rules after extend: expected %#v, actual %#v", expected, actual)
|
|
}
|
|
}
|
|
|
|
func TestVisibilityRuleSet(t *testing.T) {
|
|
t.Run("extend empty", func(t *testing.T) {
|
|
testVisibilityRuleSet(t, nil, []string{"//foo"}, []string{"//foo"})
|
|
})
|
|
t.Run("extend", func(t *testing.T) {
|
|
testVisibilityRuleSet(t, []string{"//foo"}, []string{"//bar"}, []string{"//bar", "//foo"})
|
|
})
|
|
t.Run("extend duplicate", func(t *testing.T) {
|
|
testVisibilityRuleSet(t, []string{"//foo"}, []string{"//bar", "//foo"}, []string{"//bar", "//foo"})
|
|
})
|
|
t.Run("extend public", func(t *testing.T) {
|
|
testVisibilityRuleSet(t, []string{"//visibility:public"}, []string{"//foo"}, []string{"//visibility:public"})
|
|
})
|
|
t.Run("extend private", func(t *testing.T) {
|
|
testVisibilityRuleSet(t, []string{"//visibility:private"}, []string{"//foo"}, []string{"//foo"})
|
|
})
|
|
t.Run("extend with public", func(t *testing.T) {
|
|
testVisibilityRuleSet(t, []string{"//foo"}, []string{"//visibility:public"}, []string{"//visibility:public"})
|
|
})
|
|
t.Run("extend with private", func(t *testing.T) {
|
|
t.Helper()
|
|
set := &visibilityRuleSet{[]string{"//foo"}}
|
|
err := set.Widen([]string{"//visibility:private"})
|
|
expectedError := `"//visibility:private" does not widen the visibility`
|
|
if err == nil {
|
|
t.Errorf("missing error")
|
|
} else if err.Error() != expectedError {
|
|
t.Errorf("expected error %q found error %q", expectedError, err)
|
|
}
|
|
})
|
|
}
|