blob: 36b6f35f82d31e7a708583c29fde09d4453175e3 [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//
26// Two stage process:
27// * First stage works bottom up to extract visibility information from the modules, parse it,
28// create visibilityRule structures and store them in a map keyed by the module's
29// qualifiedModuleName instance, i.e. //<pkg>:<name>. The map is stored in the context rather
30// than a global variable for testing. Each test has its own Config so they do not share a map
31// and so can be run in parallel.
32//
33// * Second stage works top down and iterates over all the deps for each module. If the dep is in
34// the same package then it is automatically visible. Otherwise, for each dep it first extracts
35// its visibilityRule from the config map. If one could not be found then it assumes that it is
36// publicly visible. Otherwise, it calls the visibility rule to check that the module can see
37// the dependency. If it cannot then an error is reported.
38//
39// TODO(b/130631145) - Make visibility work properly with prebuilts.
40// TODO(b/130796911) - Make visibility work properly with defaults.
41
42// Patterns for the values that can be specified in visibility property.
43const (
44 packagePattern = `//([^/:]+(?:/[^/:]+)*)`
45 namePattern = `:([^/:]+)`
46 visibilityRulePattern = `^(?:` + packagePattern + `)?(?:` + namePattern + `)?$`
47)
48
49var visibilityRuleRegexp = regexp.MustCompile(visibilityRulePattern)
50
51// Qualified id for a module
52type qualifiedModuleName struct {
53 // The package (i.e. directory) in which the module is defined, without trailing /
54 pkg string
55
56 // The name of the module.
57 name string
58}
59
60func (q qualifiedModuleName) String() string {
61 return fmt.Sprintf("//%s:%s", q.pkg, q.name)
62}
63
64// A visibility rule is associated with a module and determines which other modules it is visible
65// to, i.e. which other modules can depend on the rule's module.
66type visibilityRule interface {
67 // Check to see whether this rules matches m.
68 // Returns true if it does, false otherwise.
69 matches(m qualifiedModuleName) bool
70
71 String() string
72}
73
74// A compositeRule is a visibility rule composed from other visibility rules.
75// This array will only be [] if all the rules are invalid and will behave as if visibility was
76// ["//visibility:private"].
77type compositeRule []visibilityRule
78
79// A compositeRule matches if and only if any of its rules matches.
80func (c compositeRule) matches(m qualifiedModuleName) bool {
81 for _, r := range c {
82 if r.matches(m) {
83 return true
84 }
85 }
86 return false
87}
88
89func (r compositeRule) String() string {
90 s := make([]string, 0, len(r))
91 for _, r := range r {
92 s = append(s, r.String())
93 }
94
95 return "[" + strings.Join(s, ", ") + "]"
96}
97
98// A packageRule is a visibility rule that matches modules in a specific package (i.e. directory).
99type packageRule struct {
100 pkg string
101}
102
103func (r packageRule) matches(m qualifiedModuleName) bool {
104 return m.pkg == r.pkg
105}
106
107func (r packageRule) String() string {
108 return fmt.Sprintf("//%s:__pkg__", r.pkg)
109}
110
111// A subpackagesRule is a visibility rule that matches modules in a specific package (i.e.
112// directory) or any of its subpackages (i.e. subdirectories).
113type subpackagesRule struct {
114 pkgPrefix string
115}
116
117func (r subpackagesRule) matches(m qualifiedModuleName) bool {
118 return isAncestor(r.pkgPrefix, m.pkg)
119}
120
121func isAncestor(p1 string, p2 string) bool {
122 return strings.HasPrefix(p2+"/", p1+"/")
123}
124
125func (r subpackagesRule) String() string {
126 return fmt.Sprintf("//%s:__subpackages__", r.pkgPrefix)
127}
128
129var visibilityRuleMap = NewOnceKey("visibilityRuleMap")
130
131// The map from qualifiedModuleName to visibilityRule.
132func moduleToVisibilityRuleMap(ctx BaseModuleContext) *sync.Map {
133 return ctx.Config().Once(visibilityRuleMap, func() interface{} {
134 return &sync.Map{}
135 }).(*sync.Map)
136}
137
138// Visibility is not dependent on arch so this must be registered before the arch phase to avoid
139// having to process multiple variants for each module.
140func registerVisibilityRuleGatherer(ctx RegisterMutatorsContext) {
141 ctx.BottomUp("visibilityRuleGatherer", visibilityRuleGatherer).Parallel()
142}
143
144// This must be registered after the deps have been resolved.
145func registerVisibilityRuleEnforcer(ctx RegisterMutatorsContext) {
146 ctx.TopDown("visibilityRuleEnforcer", visibilityRuleEnforcer).Parallel()
147}
148
149// Gathers the visibility rules, parses the visibility properties, stores them in a map by
150// qualifiedModuleName for retrieval during enforcement.
151//
152// See ../README.md#Visibility for information on the format of the visibility rules.
153
154func visibilityRuleGatherer(ctx BottomUpMutatorContext) {
155 m, ok := ctx.Module().(Module)
156 if !ok {
157 return
158 }
159
160 qualified := createQualifiedModuleName(ctx)
161
162 visibility := m.base().commonProperties.Visibility
163 if visibility != nil {
164 rule := parseRules(ctx, qualified.pkg, visibility)
165 if rule != nil {
166 moduleToVisibilityRuleMap(ctx).Store(qualified, rule)
167 }
168 }
169}
170
171func parseRules(ctx BottomUpMutatorContext, currentPkg string, visibility []string) compositeRule {
172 ruleCount := len(visibility)
173 if ruleCount == 0 {
174 // This prohibits an empty list as its meaning is unclear, e.g. it could mean no visibility and
175 // it could mean public visibility. Requiring at least one rule makes the owner's intent
176 // clearer.
177 ctx.PropertyErrorf("visibility", "must contain at least one visibility rule")
178 return nil
179 }
180
181 rules := make(compositeRule, 0, ruleCount)
182 for _, v := range visibility {
183 ok, pkg, name := splitRule(ctx, v, currentPkg)
184 if !ok {
185 // Visibility rule is invalid so ignore it. Keep going rather than aborting straight away to
186 // ensure all the rules on this module are checked.
187 ctx.PropertyErrorf("visibility",
188 "invalid visibility pattern %q must match"+
189 " //<package>:<module>, //<package> or :<module>",
190 v)
191 continue
192 }
193
194 if pkg == "visibility" {
195 if ruleCount != 1 {
196 ctx.PropertyErrorf("visibility", "cannot mix %q with any other visibility rules", v)
197 continue
198 }
199 switch name {
200 case "private":
201 rules = append(rules, packageRule{currentPkg})
202 continue
203 case "public":
204 return nil
205 case "legacy_public":
206 ctx.PropertyErrorf("visibility", "//visibility:legacy_public must not be used")
207 return nil
208 default:
209 ctx.PropertyErrorf("visibility", "unrecognized visibility rule %q", v)
210 continue
211 }
212 }
213
214 // If the current directory is not in the vendor tree then there are some additional
215 // restrictions on the rules.
216 if !isAncestor("vendor", currentPkg) {
217 if !isAllowedFromOutsideVendor(pkg, name) {
218 ctx.PropertyErrorf("visibility",
219 "%q is not allowed. Packages outside //vendor cannot make themselves visible to specific"+
220 " targets within //vendor, they can only use //vendor:__subpackages__.", v)
221 continue
222 }
223 }
224
225 // Create the rule
226 var r visibilityRule
227 switch name {
228 case "__pkg__":
229 r = packageRule{pkg}
230 case "__subpackages__":
231 r = subpackagesRule{pkg}
232 default:
233 ctx.PropertyErrorf("visibility", "unrecognized visibility rule %q", v)
234 continue
235 }
236
237 rules = append(rules, r)
238 }
239
240 return rules
241}
242
243func isAllowedFromOutsideVendor(pkg string, name string) bool {
244 if pkg == "vendor" {
245 if name == "__subpackages__" {
246 return true
247 }
248 return false
249 }
250
251 return !isAncestor("vendor", pkg)
252}
253
254func splitRule(ctx BaseModuleContext, ruleExpression string, currentPkg string) (bool, string, string) {
255 // Make sure that the rule is of the correct format.
256 matches := visibilityRuleRegexp.FindStringSubmatch(ruleExpression)
257 if ruleExpression == "" || matches == nil {
258 return false, "", ""
259 }
260
261 // Extract the package and name.
262 pkg := matches[1]
263 name := matches[2]
264
265 // Normalize the short hands
266 if pkg == "" {
267 pkg = currentPkg
268 }
269 if name == "" {
270 name = "__pkg__"
271 }
272
273 return true, pkg, name
274}
275
276func visibilityRuleEnforcer(ctx TopDownMutatorContext) {
277 _, ok := ctx.Module().(Module)
278 if !ok {
279 return
280 }
281
282 qualified := createQualifiedModuleName(ctx)
283
284 moduleToVisibilityRule := moduleToVisibilityRuleMap(ctx)
285
286 // Visit all the dependencies making sure that this module has access to them all.
287 ctx.VisitDirectDeps(func(dep Module) {
288 depName := ctx.OtherModuleName(dep)
289 depDir := ctx.OtherModuleDir(dep)
290 depQualified := qualifiedModuleName{depDir, depName}
291
292 // Targets are always visible to other targets in their own package.
293 if depQualified.pkg == qualified.pkg {
294 return
295 }
296
297 rule, ok := moduleToVisibilityRule.Load(depQualified)
298 if ok {
299 if !rule.(compositeRule).matches(qualified) {
300 ctx.ModuleErrorf(
301 "depends on %s which is not visible to this module; %s is only visible to %s",
302 depQualified, depQualified, rule)
303 }
304 }
305 })
306}
307
308func createQualifiedModuleName(ctx BaseModuleContext) qualifiedModuleName {
309 moduleName := ctx.ModuleName()
310 dir := ctx.ModuleDir()
311 qualified := qualifiedModuleName{dir, moduleName}
312 return qualified
313}