blob: 2e01ff627140a1a578986960be107cef3421eb6b [file] [log] [blame]
Paul Duffin2e61fa62019-03-28 14:10:57 +00001// Copyright 2019 Google Inc. All rights reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package android
16
17import (
18 "fmt"
19 "regexp"
20 "strings"
21 "sync"
22)
23
24// Enforces visibility rules between modules.
25//
Paul Duffine2453c72019-05-31 14:00:04 +010026// Multi stage process:
27// * First stage works bottom up, before defaults expansion, to check the syntax of the visibility
28// rules that have been specified.
29//
30// * Second stage works bottom up to extract the package info for each package and store them in a
31// map by package name. See package.go for functionality for this.
32//
33// * Third stage works bottom up to extract visibility information from the modules, parse it,
Paul Duffin2e61fa62019-03-28 14:10:57 +000034// create visibilityRule structures and store them in a map keyed by the module's
35// qualifiedModuleName instance, i.e. //<pkg>:<name>. The map is stored in the context rather
36// than a global variable for testing. Each test has its own Config so they do not share a map
Paul Duffine2453c72019-05-31 14:00:04 +010037// and so can be run in parallel. If a module has no visibility specified then it uses the
38// default package visibility if specified.
Paul Duffin2e61fa62019-03-28 14:10:57 +000039//
Paul Duffine2453c72019-05-31 14:00:04 +010040// * Fourth stage works top down and iterates over all the deps for each module. If the dep is in
Paul Duffin2e61fa62019-03-28 14:10:57 +000041// the same package then it is automatically visible. Otherwise, for each dep it first extracts
42// its visibilityRule from the config map. If one could not be found then it assumes that it is
43// publicly visible. Otherwise, it calls the visibility rule to check that the module can see
44// the dependency. If it cannot then an error is reported.
45//
46// TODO(b/130631145) - Make visibility work properly with prebuilts.
47// TODO(b/130796911) - Make visibility work properly with defaults.
48
49// Patterns for the values that can be specified in visibility property.
50const (
51 packagePattern = `//([^/:]+(?:/[^/:]+)*)`
52 namePattern = `:([^/:]+)`
53 visibilityRulePattern = `^(?:` + packagePattern + `)?(?:` + namePattern + `)?$`
54)
55
56var visibilityRuleRegexp = regexp.MustCompile(visibilityRulePattern)
57
Paul Duffin2e61fa62019-03-28 14:10:57 +000058// A visibility rule is associated with a module and determines which other modules it is visible
59// to, i.e. which other modules can depend on the rule's module.
60type visibilityRule interface {
61 // Check to see whether this rules matches m.
62 // Returns true if it does, false otherwise.
63 matches(m qualifiedModuleName) bool
64
65 String() string
66}
67
Paul Duffine2453c72019-05-31 14:00:04 +010068// Describes the properties provided by a module that contain visibility rules.
69type visibilityPropertyImpl struct {
70 name string
71 stringsGetter func() []string
72}
73
74type visibilityProperty interface {
75 getName() string
76 getStrings() []string
77}
78
79func newVisibilityProperty(name string, stringsGetter func() []string) visibilityProperty {
80 return visibilityPropertyImpl{
81 name: name,
82 stringsGetter: stringsGetter,
83 }
84}
85
86func (p visibilityPropertyImpl) getName() string {
87 return p.name
88}
89
90func (p visibilityPropertyImpl) getStrings() []string {
91 return p.stringsGetter()
92}
93
Martin Stjernholm226b20d2019-05-17 22:42:02 +010094// A compositeRule is a visibility rule composed from a list of atomic visibility rules.
95//
96// The list corresponds to the list of strings in the visibility property after defaults expansion.
97// Even though //visibility:public is not allowed together with other rules in the visibility list
98// of a single module, it is allowed here to permit a module to override an inherited visibility
99// spec with public visibility.
100//
101// //visibility:private is not allowed in the same way, since we'd need to check for it during the
102// defaults expansion to make that work. No non-private visibility rules are allowed in a
103// compositeRule containing a privateRule.
104//
Paul Duffin2e61fa62019-03-28 14:10:57 +0000105// This array will only be [] if all the rules are invalid and will behave as if visibility was
106// ["//visibility:private"].
107type compositeRule []visibilityRule
108
109// A compositeRule matches if and only if any of its rules matches.
110func (c compositeRule) matches(m qualifiedModuleName) bool {
111 for _, r := range c {
112 if r.matches(m) {
113 return true
114 }
115 }
116 return false
117}
118
Paul Duffine2453c72019-05-31 14:00:04 +0100119func (c compositeRule) String() string {
120 s := make([]string, 0, len(c))
121 for _, r := range c {
Paul Duffin2e61fa62019-03-28 14:10:57 +0000122 s = append(s, r.String())
123 }
124
125 return "[" + strings.Join(s, ", ") + "]"
126}
127
128// A packageRule is a visibility rule that matches modules in a specific package (i.e. directory).
129type packageRule struct {
130 pkg string
131}
132
133func (r packageRule) matches(m qualifiedModuleName) bool {
134 return m.pkg == r.pkg
135}
136
137func (r packageRule) String() string {
138 return fmt.Sprintf("//%s:__pkg__", r.pkg)
139}
140
141// A subpackagesRule is a visibility rule that matches modules in a specific package (i.e.
142// directory) or any of its subpackages (i.e. subdirectories).
143type subpackagesRule struct {
144 pkgPrefix string
145}
146
147func (r subpackagesRule) matches(m qualifiedModuleName) bool {
148 return isAncestor(r.pkgPrefix, m.pkg)
149}
150
151func isAncestor(p1 string, p2 string) bool {
152 return strings.HasPrefix(p2+"/", p1+"/")
153}
154
155func (r subpackagesRule) String() string {
156 return fmt.Sprintf("//%s:__subpackages__", r.pkgPrefix)
157}
158
Martin Stjernholm226b20d2019-05-17 22:42:02 +0100159// visibilityRule for //visibility:public
160type publicRule struct{}
161
162func (r publicRule) matches(_ qualifiedModuleName) bool {
163 return true
164}
165
166func (r publicRule) String() string {
167 return "//visibility:public"
168}
169
170// visibilityRule for //visibility:private
171type privateRule struct{}
172
173func (r privateRule) matches(_ qualifiedModuleName) bool {
174 return false
175}
176
177func (r privateRule) String() string {
178 return "//visibility:private"
179}
180
Paul Duffin2e61fa62019-03-28 14:10:57 +0000181var visibilityRuleMap = NewOnceKey("visibilityRuleMap")
182
183// The map from qualifiedModuleName to visibilityRule.
184func moduleToVisibilityRuleMap(ctx BaseModuleContext) *sync.Map {
185 return ctx.Config().Once(visibilityRuleMap, func() interface{} {
186 return &sync.Map{}
187 }).(*sync.Map)
188}
189
Martin Stjernholm226b20d2019-05-17 22:42:02 +0100190// The rule checker needs to be registered before defaults expansion to correctly check that
191// //visibility:xxx isn't combined with other packages in the same list in any one module.
192func registerVisibilityRuleChecker(ctx RegisterMutatorsContext) {
193 ctx.BottomUp("visibilityRuleChecker", visibilityRuleChecker).Parallel()
194}
195
Paul Duffine2453c72019-05-31 14:00:04 +0100196// Registers the function that gathers the visibility rules for each module.
197//
Paul Duffin2e61fa62019-03-28 14:10:57 +0000198// Visibility is not dependent on arch so this must be registered before the arch phase to avoid
Martin Stjernholm226b20d2019-05-17 22:42:02 +0100199// having to process multiple variants for each module. This goes after defaults expansion to gather
Paul Duffine2453c72019-05-31 14:00:04 +0100200// the complete visibility lists from flat lists and after the package info is gathered to ensure
201// that default_visibility is available.
Paul Duffin2e61fa62019-03-28 14:10:57 +0000202func registerVisibilityRuleGatherer(ctx RegisterMutatorsContext) {
203 ctx.BottomUp("visibilityRuleGatherer", visibilityRuleGatherer).Parallel()
204}
205
206// This must be registered after the deps have been resolved.
207func registerVisibilityRuleEnforcer(ctx RegisterMutatorsContext) {
208 ctx.TopDown("visibilityRuleEnforcer", visibilityRuleEnforcer).Parallel()
209}
210
Martin Stjernholm226b20d2019-05-17 22:42:02 +0100211// Checks the per-module visibility rule lists before defaults expansion.
212func visibilityRuleChecker(ctx BottomUpMutatorContext) {
213 qualified := createQualifiedModuleName(ctx)
214 if d, ok := ctx.Module().(Defaults); ok {
215 // Defaults modules don't store the payload properties in m.base().
216 for _, props := range d.properties() {
217 if cp, ok := props.(*commonProperties); ok {
218 if visibility := cp.Visibility; visibility != nil {
Paul Duffine2453c72019-05-31 14:00:04 +0100219 checkRules(ctx, qualified.pkg, "visibility", visibility)
Martin Stjernholm226b20d2019-05-17 22:42:02 +0100220 }
221 }
222 }
223 } else if m, ok := ctx.Module().(Module); ok {
Paul Duffine2453c72019-05-31 14:00:04 +0100224 visibilityProperties := m.visibilityProperties()
225 for _, p := range visibilityProperties {
226 if visibility := p.getStrings(); visibility != nil {
227 checkRules(ctx, qualified.pkg, p.getName(), visibility)
228 }
Martin Stjernholm226b20d2019-05-17 22:42:02 +0100229 }
230 }
231}
232
Paul Duffine2453c72019-05-31 14:00:04 +0100233func checkRules(ctx BaseModuleContext, currentPkg, property string, visibility []string) {
Martin Stjernholm226b20d2019-05-17 22:42:02 +0100234 ruleCount := len(visibility)
235 if ruleCount == 0 {
236 // This prohibits an empty list as its meaning is unclear, e.g. it could mean no visibility and
237 // it could mean public visibility. Requiring at least one rule makes the owner's intent
238 // clearer.
Paul Duffine2453c72019-05-31 14:00:04 +0100239 ctx.PropertyErrorf(property, "must contain at least one visibility rule")
Martin Stjernholm226b20d2019-05-17 22:42:02 +0100240 return
241 }
242
243 for _, v := range visibility {
Paul Duffine2453c72019-05-31 14:00:04 +0100244 ok, pkg, name := splitRule(v, currentPkg)
Martin Stjernholm226b20d2019-05-17 22:42:02 +0100245 if !ok {
246 // Visibility rule is invalid so ignore it. Keep going rather than aborting straight away to
247 // ensure all the rules on this module are checked.
Paul Duffine2453c72019-05-31 14:00:04 +0100248 ctx.PropertyErrorf(property,
Martin Stjernholm226b20d2019-05-17 22:42:02 +0100249 "invalid visibility pattern %q must match"+
250 " //<package>:<module>, //<package> or :<module>",
251 v)
252 continue
253 }
254
255 if pkg == "visibility" {
256 switch name {
257 case "private", "public":
258 case "legacy_public":
Paul Duffine2453c72019-05-31 14:00:04 +0100259 ctx.PropertyErrorf(property, "//visibility:legacy_public must not be used")
Martin Stjernholm226b20d2019-05-17 22:42:02 +0100260 continue
261 default:
Paul Duffine2453c72019-05-31 14:00:04 +0100262 ctx.PropertyErrorf(property, "unrecognized visibility rule %q", v)
Martin Stjernholm226b20d2019-05-17 22:42:02 +0100263 continue
264 }
265 if ruleCount != 1 {
Paul Duffine2453c72019-05-31 14:00:04 +0100266 ctx.PropertyErrorf(property, "cannot mix %q with any other visibility rules", v)
Martin Stjernholm226b20d2019-05-17 22:42:02 +0100267 continue
268 }
269 }
270
271 // If the current directory is not in the vendor tree then there are some additional
272 // restrictions on the rules.
273 if !isAncestor("vendor", currentPkg) {
274 if !isAllowedFromOutsideVendor(pkg, name) {
Paul Duffine2453c72019-05-31 14:00:04 +0100275 ctx.PropertyErrorf(property,
Martin Stjernholm226b20d2019-05-17 22:42:02 +0100276 "%q is not allowed. Packages outside //vendor cannot make themselves visible to specific"+
277 " targets within //vendor, they can only use //vendor:__subpackages__.", v)
278 continue
279 }
280 }
281 }
282}
283
284// Gathers the flattened visibility rules after defaults expansion, parses the visibility
285// properties, stores them in a map by qualifiedModuleName for retrieval during enforcement.
Paul Duffin2e61fa62019-03-28 14:10:57 +0000286//
287// See ../README.md#Visibility for information on the format of the visibility rules.
Paul Duffin2e61fa62019-03-28 14:10:57 +0000288func visibilityRuleGatherer(ctx BottomUpMutatorContext) {
289 m, ok := ctx.Module().(Module)
290 if !ok {
291 return
292 }
293
Paul Duffine2453c72019-05-31 14:00:04 +0100294 qualifiedModuleId := m.qualifiedModuleId(ctx)
295 currentPkg := qualifiedModuleId.pkg
Paul Duffin2e61fa62019-03-28 14:10:57 +0000296
Paul Duffine2453c72019-05-31 14:00:04 +0100297 // Parse all the properties into rules and store them.
298 visibilityProperties := m.visibilityProperties()
299 for _, p := range visibilityProperties {
300 if visibility := p.getStrings(); visibility != nil {
301 rule := parseRules(ctx, currentPkg, visibility)
302 if rule != nil {
303 moduleToVisibilityRuleMap(ctx).Store(qualifiedModuleId, rule)
304 }
Paul Duffin2e61fa62019-03-28 14:10:57 +0000305 }
306 }
307}
308
Paul Duffine2453c72019-05-31 14:00:04 +0100309func parseRules(ctx BaseModuleContext, currentPkg string, visibility []string) compositeRule {
Martin Stjernholm226b20d2019-05-17 22:42:02 +0100310 rules := make(compositeRule, 0, len(visibility))
311 hasPrivateRule := false
312 hasNonPrivateRule := false
Paul Duffin2e61fa62019-03-28 14:10:57 +0000313 for _, v := range visibility {
Paul Duffine2453c72019-05-31 14:00:04 +0100314 ok, pkg, name := splitRule(v, currentPkg)
Paul Duffin2e61fa62019-03-28 14:10:57 +0000315 if !ok {
Paul Duffin2e61fa62019-03-28 14:10:57 +0000316 continue
317 }
318
Martin Stjernholm226b20d2019-05-17 22:42:02 +0100319 var r visibilityRule
320 isPrivateRule := false
Paul Duffin2e61fa62019-03-28 14:10:57 +0000321 if pkg == "visibility" {
Paul Duffin2e61fa62019-03-28 14:10:57 +0000322 switch name {
323 case "private":
Martin Stjernholm226b20d2019-05-17 22:42:02 +0100324 r = privateRule{}
325 isPrivateRule = true
Paul Duffin2e61fa62019-03-28 14:10:57 +0000326 case "public":
Martin Stjernholm226b20d2019-05-17 22:42:02 +0100327 r = publicRule{}
328 }
329 } else {
330 switch name {
331 case "__pkg__":
332 r = packageRule{pkg}
333 case "__subpackages__":
334 r = subpackagesRule{pkg}
Paul Duffin2e61fa62019-03-28 14:10:57 +0000335 default:
Paul Duffin2e61fa62019-03-28 14:10:57 +0000336 continue
337 }
338 }
339
Martin Stjernholm226b20d2019-05-17 22:42:02 +0100340 if isPrivateRule {
341 hasPrivateRule = true
342 } else {
343 hasNonPrivateRule = true
Paul Duffin2e61fa62019-03-28 14:10:57 +0000344 }
345
346 rules = append(rules, r)
347 }
348
Martin Stjernholm226b20d2019-05-17 22:42:02 +0100349 if hasPrivateRule && hasNonPrivateRule {
350 ctx.PropertyErrorf("visibility",
351 "cannot mix \"//visibility:private\" with any other visibility rules")
352 return compositeRule{privateRule{}}
353 }
354
Paul Duffin2e61fa62019-03-28 14:10:57 +0000355 return rules
356}
357
358func isAllowedFromOutsideVendor(pkg string, name string) bool {
359 if pkg == "vendor" {
360 if name == "__subpackages__" {
361 return true
362 }
363 return false
364 }
365
366 return !isAncestor("vendor", pkg)
367}
368
Paul Duffine2453c72019-05-31 14:00:04 +0100369func splitRule(ruleExpression string, currentPkg string) (bool, string, string) {
Paul Duffin2e61fa62019-03-28 14:10:57 +0000370 // Make sure that the rule is of the correct format.
371 matches := visibilityRuleRegexp.FindStringSubmatch(ruleExpression)
372 if ruleExpression == "" || matches == nil {
373 return false, "", ""
374 }
375
376 // Extract the package and name.
377 pkg := matches[1]
378 name := matches[2]
379
380 // Normalize the short hands
381 if pkg == "" {
382 pkg = currentPkg
383 }
384 if name == "" {
385 name = "__pkg__"
386 }
387
388 return true, pkg, name
389}
390
391func visibilityRuleEnforcer(ctx TopDownMutatorContext) {
Martin Stjernholm226b20d2019-05-17 22:42:02 +0100392 if _, ok := ctx.Module().(Module); !ok {
Paul Duffin2e61fa62019-03-28 14:10:57 +0000393 return
394 }
395
396 qualified := createQualifiedModuleName(ctx)
397
398 moduleToVisibilityRule := moduleToVisibilityRuleMap(ctx)
399
400 // Visit all the dependencies making sure that this module has access to them all.
401 ctx.VisitDirectDeps(func(dep Module) {
402 depName := ctx.OtherModuleName(dep)
403 depDir := ctx.OtherModuleDir(dep)
404 depQualified := qualifiedModuleName{depDir, depName}
405
406 // Targets are always visible to other targets in their own package.
407 if depQualified.pkg == qualified.pkg {
408 return
409 }
410
Paul Duffine2453c72019-05-31 14:00:04 +0100411 value, ok := moduleToVisibilityRule.Load(depQualified)
412 var rule compositeRule
Paul Duffin2e61fa62019-03-28 14:10:57 +0000413 if ok {
Paul Duffine2453c72019-05-31 14:00:04 +0100414 rule = value.(compositeRule)
415 } else {
416 packageQualifiedId := depQualified.getContainingPackageId()
417 value, ok = moduleToVisibilityRule.Load(packageQualifiedId)
418 if ok {
419 rule = value.(compositeRule)
Paul Duffin2e61fa62019-03-28 14:10:57 +0000420 }
421 }
Paul Duffine2453c72019-05-31 14:00:04 +0100422 if rule != nil && !rule.matches(qualified) {
423 ctx.ModuleErrorf("depends on %s which is not visible to this module", depQualified)
424 }
Paul Duffin2e61fa62019-03-28 14:10:57 +0000425 })
426}
427
428func createQualifiedModuleName(ctx BaseModuleContext) qualifiedModuleName {
429 moduleName := ctx.ModuleName()
430 dir := ctx.ModuleDir()
431 qualified := qualifiedModuleName{dir, moduleName}
432 return qualified
433}