1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "tools/gn/visibility.h"
7 #include "base/strings/string_piece.h"
8 #include "base/strings/string_util.h"
9 #include "tools/gn/err.h"
10 #include "tools/gn/filesystem_utils.h"
11 #include "tools/gn/item.h"
12 #include "tools/gn/label.h"
13 #include "tools/gn/scope.h"
14 #include "tools/gn/value.h"
15 #include "tools/gn/variables.h"
17 Visibility::VisPattern::VisPattern() : type_(MATCH
) {
20 Visibility::VisPattern::VisPattern(Type type
,
22 const base::StringPiece
& name
)
25 name
.CopyToString(&name_
);
28 Visibility::VisPattern::~VisPattern() {
31 bool Visibility::VisPattern::Matches(const Label
& label
) const {
34 return label
.name() == name_
&& label
.dir() == dir_
;
36 // The directories must match exactly for private visibility.
37 return label
.dir() == dir_
;
38 case RECURSIVE_DIRECTORY
:
39 // Our directory must be a prefix of the input label for recursive
40 // private visibility.
41 return label
.dir().value().compare(0, dir_
.value().size(), dir_
.value())
49 Visibility::Visibility() {
52 Visibility::~Visibility() {
55 bool Visibility::Set(const SourceDir
& current_dir
,
60 // Allow a single string to be passed in to make the common case (just one
61 // pattern) easier to specify.
62 if (value
.type() == Value::STRING
) {
63 patterns_
.push_back(GetPattern(current_dir
, value
, err
));
64 return !err
->has_error();
67 // If it's not a string, it should be a list of strings.
68 if (!value
.VerifyTypeIs(Value::LIST
, err
))
71 const std::vector
<Value
>& list
= value
.list_value();
72 for (size_t i
= 0; i
< list
.size(); i
++) {
73 patterns_
.push_back(GetPattern(current_dir
, list
[i
], err
));
80 void Visibility::SetPublic() {
83 VisPattern(VisPattern::RECURSIVE_DIRECTORY
, SourceDir(), std::string()));
86 void Visibility::SetPrivate(const SourceDir
& current_dir
) {
89 VisPattern(VisPattern::DIRECTORY
, current_dir
, std::string()));
92 bool Visibility::CanSeeMe(const Label
& label
) const {
93 for (size_t i
= 0; i
< patterns_
.size(); i
++) {
94 if (patterns_
[i
].Matches(label
))
100 std::string
Visibility::Describe(int indent
, bool include_brackets
) const {
101 std::string
outer_indent_string(indent
, ' ');
103 if (patterns_
.empty())
104 return outer_indent_string
+ "[] (no visibility)\n";
108 std::string inner_indent_string
= outer_indent_string
;
109 if (include_brackets
) {
110 result
+= outer_indent_string
+ "[\n";
111 // Indent the insides more if brackets are requested.
112 inner_indent_string
+= " ";
115 for (size_t i
= 0; i
< patterns_
.size(); i
++) {
116 switch (patterns_
[i
].type()) {
117 case VisPattern::MATCH
:
118 result
+= inner_indent_string
+
119 DirectoryWithNoLastSlash(patterns_
[i
].dir()) + ":" +
120 patterns_
[i
].name() + "\n";
122 case VisPattern::DIRECTORY
:
123 result
+= inner_indent_string
+
124 DirectoryWithNoLastSlash(patterns_
[i
].dir()) + ":*\n";
126 case VisPattern::RECURSIVE_DIRECTORY
:
127 result
+= inner_indent_string
+ patterns_
[i
].dir().value() + "*\n";
132 if (include_brackets
)
133 result
+= outer_indent_string
+ "]\n";
138 Visibility::VisPattern
Visibility::GetPattern(const SourceDir
& current_dir
,
141 if (!value
.VerifyTypeIs(Value::STRING
, err
))
144 const std::string
& str
= value
.string_value();
146 *err
= Err(value
, "Visibility pattern must not be empty.");
150 // If there's no wildcard, this is specifying an exact label, use the
151 // label resolution code to get all the implicit name stuff.
152 size_t star
= str
.find('*');
153 if (star
== std::string::npos
) {
154 Label label
= Label::Resolve(current_dir
, Label(), value
, err
);
155 if (err
->has_error())
158 // There should be no toolchain specified.
159 if (!label
.toolchain_dir().is_null() || !label
.toolchain_name().empty()) {
160 *err
= Err(value
, "Visibility label specified a toolchain.",
161 "Visibility names and patterns don't use toolchains, erase the\n"
166 return VisPattern(VisPattern::MATCH
, label
.dir(), label
.name());
169 // Wildcard case, need to split apart the label to see what it specifies.
170 base::StringPiece path
;
171 base::StringPiece name
;
172 size_t colon
= str
.find(':');
173 if (colon
== std::string::npos
) {
174 path
= base::StringPiece(str
);
176 path
= base::StringPiece(&str
[0], colon
);
177 name
= base::StringPiece(&str
[colon
+ 1], str
.size() - colon
- 1);
180 // The path can have these forms:
181 // 1. <empty> (use current dir)
182 // 2. <non wildcard stuff> (send through directory resolution)
183 // 3. <non wildcard stuff>* (send stuff through dir resolution, note star)
184 // 4. * (matches anything)
186 bool has_path_star
= false;
188 // Looks like ":foo".
190 } else if (path
[path
.size() - 1] == '*') {
191 // Case 3 or 4 above.
192 has_path_star
= true;
194 // Adjust path to contain everything but the star.
195 path
= path
.substr(0, path
.size() - 1);
197 if (!path
.empty() && path
[path
.size() - 1] != '/') {
198 // The input was "foo*" which is invalid.
199 *err
= Err(value
, "'*' must match full directories in visibility.",
200 "You did \"foo*\" but visibility doesn't do general pattern\n"
201 "matching. Instead, you have to add a slash: \"foo/*\".");
206 // Resolve the part of the path that's not the wildcard.
208 // The non-wildcard stuff better not have a wildcard.
209 if (path
.find('*') != base::StringPiece::npos
) {
210 *err
= Err(value
, "Visibility patterns only support wildcard suffixes.",
211 "The visibility pattern contained a '*' that wasn't at tne end.");
215 // Resolve the non-wildcard stuff.
216 dir
= current_dir
.ResolveRelativeDir(path
);
218 *err
= Err(value
, "Visibility pattern didn't resolve to a dir.",
219 "The directory name \"" + path
.as_string() + "\" didn't\n"
220 "resolve to a directory.");
225 // Resolve the name. At this point, we're doing wildcard matches so the
226 // name should either be empty ("foo/*") or a wildcard ("foo:*");
227 if (colon
!= std::string::npos
&& name
!= "*") {
228 *err
= Err(value
, "Invalid visibility pattern.",
229 "You seem to be using the wildcard more generally that is supported.\n"
230 "Did you mean \"foo:*\" to match everything in the current file, or\n"
231 "\"./*\" to recursively match everything in the currend subtree.");
235 VisPattern::Type type
;
237 // We know there's a wildcard, so if the name is empty it looks like
239 type
= VisPattern::RECURSIVE_DIRECTORY
;
241 // Everything else should be of the form "foo:*".
242 type
= VisPattern::DIRECTORY
;
245 // When we're doing wildcard matching, the name is always empty.
246 return VisPattern(type
, dir
, base::StringPiece());
250 bool Visibility::CheckItemVisibility(const Item
* from
,
253 if (!to
->visibility().CanSeeMe(from
->label())) {
254 std::string to_label
= to
->label().GetUserVisibleName(false);
255 *err
= Err(from
->defined_from(), "Dependency not allowed.",
256 "The item " + from
->label().GetUserVisibleName(false) + "\n"
257 "can not depend on " + to_label
+ "\n"
258 "because it is not in " + to_label
+ "'s visibility list: " +
259 to
->visibility().Describe(0, true));
266 bool Visibility::FillItemVisibility(Item
* item
, Scope
* scope
, Err
* err
) {
267 const Value
* vis_value
= scope
->GetValue(variables::kVisibility
, true);
269 item
->visibility().Set(scope
->GetSourceDir(), *vis_value
, err
);
270 else // Default to public.
271 item
->visibility().SetPublic();
272 return !err
->has_error();