blob: c28ec93f4df23ba578cc87764edb1ab0197af247 [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 {
Paul Duffin63c6e182019-07-24 14:24:38 +010070 name string
71 stringsProperty *[]string
Paul Duffine2453c72019-05-31 14:00:04 +010072}
73
74type visibilityProperty interface {
75 getName() string
76 getStrings() []string
77}
78
Paul Duffin63c6e182019-07-24 14:24:38 +010079func newVisibilityProperty(name string, stringsProperty *[]string) visibilityProperty {
Paul Duffine2453c72019-05-31 14:00:04 +010080 return visibilityPropertyImpl{
Paul Duffin63c6e182019-07-24 14:24:38 +010081 name: name,
82 stringsProperty: stringsProperty,
Paul Duffine2453c72019-05-31 14:00:04 +010083 }
84}
85
86func (p visibilityPropertyImpl) getName() string {
87 return p.name
88}
89
90func (p visibilityPropertyImpl) getStrings() []string {
Paul Duffin63c6e182019-07-24 14:24:38 +010091 return *p.stringsProperty
Paul Duffine2453c72019-05-31 14:00:04 +010092}
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 {
Paul Duffin593b3c92019-12-05 14:31:48 +0000120 return "[" + strings.Join(c.Strings(), ", ") + "]"
121}
122
123func (c compositeRule) Strings() []string {
Paul Duffine2453c72019-05-31 14:00:04 +0100124 s := make([]string, 0, len(c))
125 for _, r := range c {
Paul Duffin2e61fa62019-03-28 14:10:57 +0000126 s = append(s, r.String())
127 }
Paul Duffin593b3c92019-12-05 14:31:48 +0000128 return s
Paul Duffin2e61fa62019-03-28 14:10:57 +0000129}
130
131// A packageRule is a visibility rule that matches modules in a specific package (i.e. directory).
132type packageRule struct {
133 pkg string
134}
135
136func (r packageRule) matches(m qualifiedModuleName) bool {
137 return m.pkg == r.pkg
138}
139
140func (r packageRule) String() string {
141 return fmt.Sprintf("//%s:__pkg__", r.pkg)
142}
143
144// A subpackagesRule is a visibility rule that matches modules in a specific package (i.e.
145// directory) or any of its subpackages (i.e. subdirectories).
146type subpackagesRule struct {
147 pkgPrefix string
148}
149
150func (r subpackagesRule) matches(m qualifiedModuleName) bool {
151 return isAncestor(r.pkgPrefix, m.pkg)
152}
153
154func isAncestor(p1 string, p2 string) bool {
155 return strings.HasPrefix(p2+"/", p1+"/")
156}
157
158func (r subpackagesRule) String() string {
159 return fmt.Sprintf("//%s:__subpackages__", r.pkgPrefix)
160}
161
Martin Stjernholm226b20d2019-05-17 22:42:02 +0100162// visibilityRule for //visibility:public
163type publicRule struct{}
164
165func (r publicRule) matches(_ qualifiedModuleName) bool {
166 return true
167}
168
169func (r publicRule) String() string {
170 return "//visibility:public"
171}
172
173// visibilityRule for //visibility:private
174type privateRule struct{}
175
176func (r privateRule) matches(_ qualifiedModuleName) bool {
177 return false
178}
179
180func (r privateRule) String() string {
181 return "//visibility:private"
182}
183
Paul Duffin2e61fa62019-03-28 14:10:57 +0000184var visibilityRuleMap = NewOnceKey("visibilityRuleMap")
185
186// The map from qualifiedModuleName to visibilityRule.
187func moduleToVisibilityRuleMap(ctx BaseModuleContext) *sync.Map {
188 return ctx.Config().Once(visibilityRuleMap, func() interface{} {
189 return &sync.Map{}
190 }).(*sync.Map)
191}
192
Martin Stjernholm226b20d2019-05-17 22:42:02 +0100193// The rule checker needs to be registered before defaults expansion to correctly check that
194// //visibility:xxx isn't combined with other packages in the same list in any one module.
Paul Duffin593b3c92019-12-05 14:31:48 +0000195func RegisterVisibilityRuleChecker(ctx RegisterMutatorsContext) {
Martin Stjernholm226b20d2019-05-17 22:42:02 +0100196 ctx.BottomUp("visibilityRuleChecker", visibilityRuleChecker).Parallel()
197}
198
Paul Duffine2453c72019-05-31 14:00:04 +0100199// Registers the function that gathers the visibility rules for each module.
200//
Paul Duffin2e61fa62019-03-28 14:10:57 +0000201// Visibility is not dependent on arch so this must be registered before the arch phase to avoid
Martin Stjernholm226b20d2019-05-17 22:42:02 +0100202// having to process multiple variants for each module. This goes after defaults expansion to gather
Paul Duffine2453c72019-05-31 14:00:04 +0100203// the complete visibility lists from flat lists and after the package info is gathered to ensure
204// that default_visibility is available.
Paul Duffin593b3c92019-12-05 14:31:48 +0000205func RegisterVisibilityRuleGatherer(ctx RegisterMutatorsContext) {
Paul Duffin2e61fa62019-03-28 14:10:57 +0000206 ctx.BottomUp("visibilityRuleGatherer", visibilityRuleGatherer).Parallel()
207}
208
209// This must be registered after the deps have been resolved.
Paul Duffin593b3c92019-12-05 14:31:48 +0000210func RegisterVisibilityRuleEnforcer(ctx RegisterMutatorsContext) {
Paul Duffin2e61fa62019-03-28 14:10:57 +0000211 ctx.TopDown("visibilityRuleEnforcer", visibilityRuleEnforcer).Parallel()
212}
213
Martin Stjernholm226b20d2019-05-17 22:42:02 +0100214// Checks the per-module visibility rule lists before defaults expansion.
215func visibilityRuleChecker(ctx BottomUpMutatorContext) {
216 qualified := createQualifiedModuleName(ctx)
Paul Duffin63c6e182019-07-24 14:24:38 +0100217 if m, ok := ctx.Module().(Module); ok {
Paul Duffine2453c72019-05-31 14:00:04 +0100218 visibilityProperties := m.visibilityProperties()
219 for _, p := range visibilityProperties {
220 if visibility := p.getStrings(); visibility != nil {
221 checkRules(ctx, qualified.pkg, p.getName(), visibility)
222 }
Martin Stjernholm226b20d2019-05-17 22:42:02 +0100223 }
224 }
225}
226
Paul Duffine2453c72019-05-31 14:00:04 +0100227func checkRules(ctx BaseModuleContext, currentPkg, property string, visibility []string) {
Martin Stjernholm226b20d2019-05-17 22:42:02 +0100228 ruleCount := len(visibility)
229 if ruleCount == 0 {
230 // This prohibits an empty list as its meaning is unclear, e.g. it could mean no visibility and
231 // it could mean public visibility. Requiring at least one rule makes the owner's intent
232 // clearer.
Paul Duffine2453c72019-05-31 14:00:04 +0100233 ctx.PropertyErrorf(property, "must contain at least one visibility rule")
Martin Stjernholm226b20d2019-05-17 22:42:02 +0100234 return
235 }
236
237 for _, v := range visibility {
Paul Duffine2453c72019-05-31 14:00:04 +0100238 ok, pkg, name := splitRule(v, currentPkg)
Martin Stjernholm226b20d2019-05-17 22:42:02 +0100239 if !ok {
240 // Visibility rule is invalid so ignore it. Keep going rather than aborting straight away to
241 // ensure all the rules on this module are checked.
Paul Duffine2453c72019-05-31 14:00:04 +0100242 ctx.PropertyErrorf(property,
Martin Stjernholm226b20d2019-05-17 22:42:02 +0100243 "invalid visibility pattern %q must match"+
244 " //<package>:<module>, //<package> or :<module>",
245 v)
246 continue
247 }
248
249 if pkg == "visibility" {
250 switch name {
251 case "private", "public":
252 case "legacy_public":
Paul Duffine2453c72019-05-31 14:00:04 +0100253 ctx.PropertyErrorf(property, "//visibility:legacy_public must not be used")
Martin Stjernholm226b20d2019-05-17 22:42:02 +0100254 continue
255 default:
Paul Duffine2453c72019-05-31 14:00:04 +0100256 ctx.PropertyErrorf(property, "unrecognized visibility rule %q", v)
Martin Stjernholm226b20d2019-05-17 22:42:02 +0100257 continue
258 }
259 if ruleCount != 1 {
Paul Duffine2453c72019-05-31 14:00:04 +0100260 ctx.PropertyErrorf(property, "cannot mix %q with any other visibility rules", v)
Martin Stjernholm226b20d2019-05-17 22:42:02 +0100261 continue
262 }
263 }
264
265 // If the current directory is not in the vendor tree then there are some additional
266 // restrictions on the rules.
267 if !isAncestor("vendor", currentPkg) {
268 if !isAllowedFromOutsideVendor(pkg, name) {
Paul Duffine2453c72019-05-31 14:00:04 +0100269 ctx.PropertyErrorf(property,
Martin Stjernholm226b20d2019-05-17 22:42:02 +0100270 "%q is not allowed. Packages outside //vendor cannot make themselves visible to specific"+
271 " targets within //vendor, they can only use //vendor:__subpackages__.", v)
272 continue
273 }
274 }
275 }
276}
277
278// Gathers the flattened visibility rules after defaults expansion, parses the visibility
279// properties, stores them in a map by qualifiedModuleName for retrieval during enforcement.
Paul Duffin2e61fa62019-03-28 14:10:57 +0000280//
281// See ../README.md#Visibility for information on the format of the visibility rules.
Paul Duffin2e61fa62019-03-28 14:10:57 +0000282func visibilityRuleGatherer(ctx BottomUpMutatorContext) {
283 m, ok := ctx.Module().(Module)
284 if !ok {
285 return
286 }
287
Paul Duffine2453c72019-05-31 14:00:04 +0100288 qualifiedModuleId := m.qualifiedModuleId(ctx)
289 currentPkg := qualifiedModuleId.pkg
Paul Duffin2e61fa62019-03-28 14:10:57 +0000290
Paul Duffin63c6e182019-07-24 14:24:38 +0100291 // Parse the visibility rules that control access to the module and store them by id
292 // for use when enforcing the rules.
293 if visibility := m.visibility(); visibility != nil {
294 rule := parseRules(ctx, currentPkg, m.visibility())
295 if rule != nil {
296 moduleToVisibilityRuleMap(ctx).Store(qualifiedModuleId, rule)
Paul Duffin2e61fa62019-03-28 14:10:57 +0000297 }
298 }
299}
300
Paul Duffine2453c72019-05-31 14:00:04 +0100301func parseRules(ctx BaseModuleContext, currentPkg string, visibility []string) compositeRule {
Martin Stjernholm226b20d2019-05-17 22:42:02 +0100302 rules := make(compositeRule, 0, len(visibility))
303 hasPrivateRule := false
304 hasNonPrivateRule := false
Paul Duffin2e61fa62019-03-28 14:10:57 +0000305 for _, v := range visibility {
Paul Duffine2453c72019-05-31 14:00:04 +0100306 ok, pkg, name := splitRule(v, currentPkg)
Paul Duffin2e61fa62019-03-28 14:10:57 +0000307 if !ok {
Paul Duffin2e61fa62019-03-28 14:10:57 +0000308 continue
309 }
310
Martin Stjernholm226b20d2019-05-17 22:42:02 +0100311 var r visibilityRule
312 isPrivateRule := false
Paul Duffin2e61fa62019-03-28 14:10:57 +0000313 if pkg == "visibility" {
Paul Duffin2e61fa62019-03-28 14:10:57 +0000314 switch name {
315 case "private":
Martin Stjernholm226b20d2019-05-17 22:42:02 +0100316 r = privateRule{}
317 isPrivateRule = true
Paul Duffin2e61fa62019-03-28 14:10:57 +0000318 case "public":
Martin Stjernholm226b20d2019-05-17 22:42:02 +0100319 r = publicRule{}
320 }
321 } else {
322 switch name {
323 case "__pkg__":
324 r = packageRule{pkg}
325 case "__subpackages__":
326 r = subpackagesRule{pkg}
Paul Duffin2e61fa62019-03-28 14:10:57 +0000327 default:
Paul Duffin2e61fa62019-03-28 14:10:57 +0000328 continue
329 }
330 }
331
Martin Stjernholm226b20d2019-05-17 22:42:02 +0100332 if isPrivateRule {
333 hasPrivateRule = true
334 } else {
335 hasNonPrivateRule = true
Paul Duffin2e61fa62019-03-28 14:10:57 +0000336 }
337
338 rules = append(rules, r)
339 }
340
Martin Stjernholm226b20d2019-05-17 22:42:02 +0100341 if hasPrivateRule && hasNonPrivateRule {
342 ctx.PropertyErrorf("visibility",
343 "cannot mix \"//visibility:private\" with any other visibility rules")
344 return compositeRule{privateRule{}}
345 }
346
Paul Duffin2e61fa62019-03-28 14:10:57 +0000347 return rules
348}
349
350func isAllowedFromOutsideVendor(pkg string, name string) bool {
351 if pkg == "vendor" {
352 if name == "__subpackages__" {
353 return true
354 }
355 return false
356 }
357
358 return !isAncestor("vendor", pkg)
359}
360
Paul Duffine2453c72019-05-31 14:00:04 +0100361func splitRule(ruleExpression string, currentPkg string) (bool, string, string) {
Paul Duffin2e61fa62019-03-28 14:10:57 +0000362 // Make sure that the rule is of the correct format.
363 matches := visibilityRuleRegexp.FindStringSubmatch(ruleExpression)
364 if ruleExpression == "" || matches == nil {
365 return false, "", ""
366 }
367
368 // Extract the package and name.
369 pkg := matches[1]
370 name := matches[2]
371
372 // Normalize the short hands
373 if pkg == "" {
374 pkg = currentPkg
375 }
376 if name == "" {
377 name = "__pkg__"
378 }
379
380 return true, pkg, name
381}
382
383func visibilityRuleEnforcer(ctx TopDownMutatorContext) {
Martin Stjernholm226b20d2019-05-17 22:42:02 +0100384 if _, ok := ctx.Module().(Module); !ok {
Paul Duffin2e61fa62019-03-28 14:10:57 +0000385 return
386 }
387
388 qualified := createQualifiedModuleName(ctx)
389
Paul Duffin2e61fa62019-03-28 14:10:57 +0000390 // Visit all the dependencies making sure that this module has access to them all.
391 ctx.VisitDirectDeps(func(dep Module) {
392 depName := ctx.OtherModuleName(dep)
393 depDir := ctx.OtherModuleDir(dep)
394 depQualified := qualifiedModuleName{depDir, depName}
395
396 // Targets are always visible to other targets in their own package.
397 if depQualified.pkg == qualified.pkg {
398 return
399 }
400
Paul Duffin593b3c92019-12-05 14:31:48 +0000401 rule := effectiveVisibilityRules(ctx, depQualified)
Paul Duffine2453c72019-05-31 14:00:04 +0100402 if rule != nil && !rule.matches(qualified) {
403 ctx.ModuleErrorf("depends on %s which is not visible to this module", depQualified)
404 }
Paul Duffin2e61fa62019-03-28 14:10:57 +0000405 })
406}
407
Paul Duffin593b3c92019-12-05 14:31:48 +0000408func effectiveVisibilityRules(ctx BaseModuleContext, qualified qualifiedModuleName) compositeRule {
409 moduleToVisibilityRule := moduleToVisibilityRuleMap(ctx)
410 value, ok := moduleToVisibilityRule.Load(qualified)
411 var rule compositeRule
412 if ok {
413 rule = value.(compositeRule)
414 } else {
415 rule = packageDefaultVisibility(ctx, qualified)
416 }
417 return rule
418}
419
Paul Duffin2e61fa62019-03-28 14:10:57 +0000420func createQualifiedModuleName(ctx BaseModuleContext) qualifiedModuleName {
421 moduleName := ctx.ModuleName()
422 dir := ctx.ModuleDir()
423 qualified := qualifiedModuleName{dir, moduleName}
424 return qualified
425}
Paul Duffine484f472019-06-20 16:38:08 +0100426
427func packageDefaultVisibility(ctx BaseModuleContext, moduleId qualifiedModuleName) compositeRule {
428 moduleToVisibilityRule := moduleToVisibilityRuleMap(ctx)
429 packageQualifiedId := moduleId.getContainingPackageId()
430 for {
431 value, ok := moduleToVisibilityRule.Load(packageQualifiedId)
432 if ok {
433 return value.(compositeRule)
434 }
435
436 if packageQualifiedId.isRootPackage() {
437 return nil
438 }
439
440 packageQualifiedId = packageQualifiedId.getContainingPackageId()
441 }
442}
Paul Duffin593b3c92019-12-05 14:31:48 +0000443
444// Get the effective visibility rules, i.e. the actual rules that affect the visibility of the
445// property irrespective of where they are defined.
446//
447// Includes visibility rules specified by package default_visibility and/or on defaults.
448// Short hand forms, e.g. //:__subpackages__ are replaced with their full form, e.g.
449// //package/containing/rule:__subpackages__.
450func EffectiveVisibilityRules(ctx BaseModuleContext, module Module) []string {
451 moduleName := ctx.OtherModuleName(module)
452 dir := ctx.OtherModuleDir(module)
453 qualified := qualifiedModuleName{dir, moduleName}
454
455 rule := effectiveVisibilityRules(ctx, qualified)
456
457 return rule.Strings()
458}