blob: 6fc8fa125dc89eff85c630619acc263d332deb2f [file] [log] [blame]
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "tools/gn/visibility.h"
#include "base/strings/string_piece.h"
#include "base/strings/string_util.h"
#include "tools/gn/err.h"
#include "tools/gn/filesystem_utils.h"
#include "tools/gn/item.h"
#include "tools/gn/label.h"
#include "tools/gn/scope.h"
#include "tools/gn/value.h"
#include "tools/gn/variables.h"
Visibility::VisPattern::VisPattern() : type_(MATCH) {
}
Visibility::VisPattern::VisPattern(Type type,
const SourceDir& dir,
const base::StringPiece& name)
: type_(type),
dir_(dir) {
name.CopyToString(&name_);
}
Visibility::VisPattern::~VisPattern() {
}
bool Visibility::VisPattern::Matches(const Label& label) const {
switch (type_) {
case MATCH:
return label.name() == name_ && label.dir() == dir_;
case DIRECTORY:
// The directories must match exactly for private visibility.
return label.dir() == dir_;
case RECURSIVE_DIRECTORY:
// Our directory must be a prefix of the input label for recursive
// private visibility.
return label.dir().value().compare(0, dir_.value().size(), dir_.value())
== 0;
default:
NOTREACHED();
return false;
}
}
Visibility::Visibility() {
}
Visibility::~Visibility() {
}
bool Visibility::Set(const SourceDir& current_dir,
const Value& value,
Err* err) {
patterns_.clear();
// Allow a single string to be passed in to make the common case (just one
// pattern) easier to specify.
if (value.type() == Value::STRING) {
patterns_.push_back(GetPattern(current_dir, value, err));
return !err->has_error();
}
// If it's not a string, it should be a list of strings.
if (!value.VerifyTypeIs(Value::LIST, err))
return false;
const std::vector<Value>& list = value.list_value();
for (size_t i = 0; i < list.size(); i++) {
patterns_.push_back(GetPattern(current_dir, list[i], err));
if (err->has_error())
return false;
}
return true;
}
void Visibility::SetPublic() {
patterns_.clear();
patterns_.push_back(
VisPattern(VisPattern::RECURSIVE_DIRECTORY, SourceDir(), std::string()));
}
void Visibility::SetPrivate(const SourceDir& current_dir) {
patterns_.clear();
patterns_.push_back(
VisPattern(VisPattern::DIRECTORY, current_dir, std::string()));
}
bool Visibility::CanSeeMe(const Label& label) const {
for (size_t i = 0; i < patterns_.size(); i++) {
if (patterns_[i].Matches(label))
return true;
}
return false;
}
std::string Visibility::Describe(int indent, bool include_brackets) const {
std::string outer_indent_string(indent, ' ');
if (patterns_.empty())
return outer_indent_string + "[] (no visibility)\n";
std::string result;
std::string inner_indent_string = outer_indent_string;
if (include_brackets) {
result += outer_indent_string + "[\n";
// Indent the insides more if brackets are requested.
inner_indent_string += " ";
}
for (size_t i = 0; i < patterns_.size(); i++) {
switch (patterns_[i].type()) {
case VisPattern::MATCH:
result += inner_indent_string +
DirectoryWithNoLastSlash(patterns_[i].dir()) + ":" +
patterns_[i].name() + "\n";
break;
case VisPattern::DIRECTORY:
result += inner_indent_string +
DirectoryWithNoLastSlash(patterns_[i].dir()) + ":*\n";
break;
case VisPattern::RECURSIVE_DIRECTORY:
result += inner_indent_string + patterns_[i].dir().value() + "*\n";
break;
}
}
if (include_brackets)
result += outer_indent_string + "]\n";
return result;
}
// static
Visibility::VisPattern Visibility::GetPattern(const SourceDir& current_dir,
const Value& value,
Err* err) {
if (!value.VerifyTypeIs(Value::STRING, err))
return VisPattern();
const std::string& str = value.string_value();
if (str.empty()) {
*err = Err(value, "Visibility pattern must not be empty.");
return VisPattern();
}
// If there's no wildcard, this is specifying an exact label, use the
// label resolution code to get all the implicit name stuff.
size_t star = str.find('*');
if (star == std::string::npos) {
Label label = Label::Resolve(current_dir, Label(), value, err);
if (err->has_error())
return VisPattern();
// There should be no toolchain specified.
if (!label.toolchain_dir().is_null() || !label.toolchain_name().empty()) {
*err = Err(value, "Visibility label specified a toolchain.",
"Visibility names and patterns don't use toolchains, erase the\n"
"stuff in the ().");
return VisPattern();
}
return VisPattern(VisPattern::MATCH, label.dir(), label.name());
}
// Wildcard case, need to split apart the label to see what it specifies.
base::StringPiece path;
base::StringPiece name;
size_t colon = str.find(':');
if (colon == std::string::npos) {
path = base::StringPiece(str);
} else {
path = base::StringPiece(&str[0], colon);
name = base::StringPiece(&str[colon + 1], str.size() - colon - 1);
}
// The path can have these forms:
// 1. <empty> (use current dir)
// 2. <non wildcard stuff> (send through directory resolution)
// 3. <non wildcard stuff>* (send stuff through dir resolution, note star)
// 4. * (matches anything)
SourceDir dir;
bool has_path_star = false;
if (path.empty()) {
// Looks like ":foo".
dir = current_dir;
} else if (path[path.size() - 1] == '*') {
// Case 3 or 4 above.
has_path_star = true;
// Adjust path to contain everything but the star.
path = path.substr(0, path.size() - 1);
if (!path.empty() && path[path.size() - 1] != '/') {
// The input was "foo*" which is invalid.
*err = Err(value, "'*' must match full directories in visibility.",
"You did \"foo*\" but visibility doesn't do general pattern\n"
"matching. Instead, you have to add a slash: \"foo/*\".");
return VisPattern();
}
}
// Resolve the part of the path that's not the wildcard.
if (!path.empty()) {
// The non-wildcard stuff better not have a wildcard.
if (path.find('*') != base::StringPiece::npos) {
*err = Err(value, "Visibility patterns only support wildcard suffixes.",
"The visibility pattern contained a '*' that wasn't at tne end.");
return VisPattern();
}
// Resolve the non-wildcard stuff.
dir = current_dir.ResolveRelativeDir(path);
if (dir.is_null()) {
*err = Err(value, "Visibility pattern didn't resolve to a dir.",
"The directory name \"" + path.as_string() + "\" didn't\n"
"resolve to a directory.");
return VisPattern();
}
}
// Resolve the name. At this point, we're doing wildcard matches so the
// name should either be empty ("foo/*") or a wildcard ("foo:*");
if (colon != std::string::npos && name != "*") {
*err = Err(value, "Invalid visibility pattern.",
"You seem to be using the wildcard more generally that is supported.\n"
"Did you mean \"foo:*\" to match everything in the current file, or\n"
"\"./*\" to recursively match everything in the currend subtree.");
return VisPattern();
}
VisPattern::Type type;
if (has_path_star) {
// We know there's a wildcard, so if the name is empty it looks like
// "foo/*".
type = VisPattern::RECURSIVE_DIRECTORY;
} else {
// Everything else should be of the form "foo:*".
type = VisPattern::DIRECTORY;
}
// When we're doing wildcard matching, the name is always empty.
return VisPattern(type, dir, base::StringPiece());
}
// static
bool Visibility::CheckItemVisibility(const Item* from,
const Item* to,
Err* err) {
if (!to->visibility().CanSeeMe(from->label())) {
std::string to_label = to->label().GetUserVisibleName(false);
*err = Err(from->defined_from(), "Dependency not allowed.",
"The item " + from->label().GetUserVisibleName(false) + "\n"
"can not depend on " + to_label + "\n"
"because it is not in " + to_label + "'s visibility list: " +
to->visibility().Describe(0, true));
return false;
}
return true;
}
// static
bool Visibility::FillItemVisibility(Item* item, Scope* scope, Err* err) {
const Value* vis_value = scope->GetValue(variables::kVisibility, true);
if (vis_value)
item->visibility().Set(scope->GetSourceDir(), *vis_value, err);
else // Default to public.
item->visibility().SetPublic();
return !err->has_error();
}