Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(46)

Unified Diff: tools/gn/label.cc

Issue 21114002: Add initial prototype for the GN meta-buildsystem. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: add owners and readme Created 7 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « tools/gn/label.h ('k') | tools/gn/label_unittest.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: tools/gn/label.cc
diff --git a/tools/gn/label.cc b/tools/gn/label.cc
new file mode 100644
index 0000000000000000000000000000000000000000..f9b48da3c77eb54856cb80bd98d0714ea40119d2
--- /dev/null
+++ b/tools/gn/label.cc
@@ -0,0 +1,263 @@
+// Copyright (c) 2013 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/label.h"
+
+#include "base/logging.h"
+#include "tools/gn/err.h"
+#include "tools/gn/parse_tree.h"
+#include "tools/gn/value.h"
+
+namespace {
+
+// We print user visible label names with no trailing slash after the
+// directory name.
+std::string DirWithNoTrailingSlash(const SourceDir& dir) {
+ // Be careful not to trim if the input is just "/" or "//".
+ if (dir.value().size() > 2)
+ return dir.value().substr(0, dir.value().size() - 1);
+ return dir.value();
+}
+
+// Given the separate-out input (everything before the colon) in the dep rule,
+// computes the final build rule. Sets err on failure. On success,
+// |*used_implicit| will be set to whether the implicit current directory was
+// used. The value is used only for generating error messages.
+bool ComputeBuildLocationFromDep(const Value& input_value,
+ const SourceDir& current_dir,
+ const base::StringPiece& input,
+ SourceDir* result,
+ Err* err) {
+ // No rule, use the current locaton.
+ if (input.empty()) {
+ *result = current_dir;
+ return true;
+ }
+
+ // Don't allow directories to start with a single slash. All labels must be
+ // in the source root.
+ if (input[0] == '/' && (input.size() == 1 || input[1] != '/')) {
+ *err = Err(input_value, "Label can't start with a single slash",
+ "Labels must be either relative (no slash at the beginning) or be "
+ "absolute\ninside the source root (two slashes at the beginning).");
+ return false;
+ }
+
+ *result = current_dir.ResolveRelativeDir(input);
+ return true;
+}
+
+// Given the separated-out target name (after the colon) computes the final
+// name, using the implicit name from the previously-generated
+// computed_location if necessary. The input_value is used only for generating
+// error messages.
+bool ComputeTargetNameFromDep(const Value& input_value,
+ const SourceDir& computed_location,
+ const base::StringPiece& input,
+ std::string* result,
+ Err* err) {
+ if (!input.empty()) {
+ // Easy case: input is specified, just use it.
+ result->assign(input.data(), input.size());
+ return true;
+ }
+
+ const std::string& loc = computed_location.value();
+
+ // Use implicit name. The path will be "//", "//base/", "//base/i18n/", etc.
+ if (loc.size() <= 1) {
+ *err = Err(input_value, "This dependency name is empty");
+ return false;
+ }
+
+ size_t next_to_last_slash = loc.rfind('/', loc.size() - 2);
+ DCHECK(next_to_last_slash != std::string::npos);
+ result->assign(&loc[next_to_last_slash + 1],
+ loc.size() - next_to_last_slash - 2);
+ return true;
+}
+
+// The original value is used only for error reporting, use the |input| as the
+// input to this function (which may be a substring of the original value when
+// we're parsing toolchains.
+//
+// If the output toolchain vars are NULL, then we'll report an error if we
+// find a toolchain specified (this is used when recursively parsing toolchain
+// labels which themselves can't have toolchain specs).
+//
+// We assume that the output variables are initialized to empty so we don't
+// write them unless we need them to contain something.
+//
+// Returns true on success. On failure, the out* variables might be written to
+// but shouldn't be used.
+bool Resolve(const SourceDir& current_dir,
+ const Label& current_toolchain,
+ const Value& original_value,
+ const base::StringPiece& input,
+ SourceDir* out_dir,
+ std::string* out_name,
+ SourceDir* out_toolchain_dir,
+ std::string* out_toolchain_name,
+ Err* err) {
+ // To workaround the problem that StringPiece operator[] doesn't return a ref.
+ const char* input_str = input.data();
+
+ size_t path_separator = input.find_first_of(":(");
+ base::StringPiece location_piece;
+ base::StringPiece name_piece;
+ base::StringPiece toolchain_piece;
+ if (path_separator == std::string::npos) {
+ location_piece = input;
+ // Leave name & toolchain piece null.
+ } else {
+ location_piece = base::StringPiece(&input_str[0], path_separator);
+
+ size_t toolchain_separator = input.find('(', path_separator);
+ if (toolchain_separator == std::string::npos) {
+ name_piece = base::StringPiece(&input_str[path_separator + 1],
+ input.size() - path_separator - 1);
+ // Leave location piece null.
+ } else if (!out_toolchain_dir) {
+ // Toolchain specified but not allows in this context.
+ *err = Err(original_value, "Toolchain has a toolchain.",
+ "Your toolchain definition (inside the parens) seems to itself "
+ "have a\ntoolchain. Don't do this.");
+ return false;
+ } else {
+ // Name piece is everything between the two separators. Note that the
+ // separators may be the same (e.g. "//foo(bar)" which means empty name.
+ if (toolchain_separator > path_separator) {
+ name_piece = base::StringPiece(
+ &input_str[path_separator + 1],
+ toolchain_separator - path_separator - 1);
+ }
+
+ // Toolchain name should end in a ) and this should be the end of the
+ // string.
+ if (input[input.size() - 1] != ')') {
+ *err = Err(original_value, "Bad toolchain name.",
+ "Toolchain name must end in a \")\" at the end of the label.");
+ return false;
+ }
+
+ // Subtract off the two parens to just get the toolchain name.
+ toolchain_piece = base::StringPiece(
+ &input_str[toolchain_separator + 1],
+ input.size() - toolchain_separator - 2);
+ }
+ }
+
+ // Everything before the separator is the filename.
+ // We allow three cases:
+ // Absolute: "//foo:bar" -> /foo:bar
+ // Target in current file: ":foo" -> <currentdir>:foo
+ // Path with implicit name: "/foo" -> /foo:foo
+ if (location_piece.empty() && name_piece.empty()) {
+ // Can't use both implicit filename and name (":").
+ *err = Err(original_value, "This doesn't specify a dependency.");
+ return false;
+ }
+
+ if (!ComputeBuildLocationFromDep(original_value, current_dir, location_piece,
+ out_dir, err))
+ return false;
+
+ if (!ComputeTargetNameFromDep(original_value, *out_dir, name_piece,
+ out_name, err))
+ return false;
+
+ // Last, do the toolchains.
+ if (out_toolchain_dir) {
+ // Handle empty toolchain strings. We don't allow normal labels to be
+ // empty so we can't allow the recursive call of this function to do this
+ // check.
+ if (toolchain_piece.empty()) {
+ *out_toolchain_dir = current_toolchain.dir();
+ *out_toolchain_name = current_toolchain.name();
+ return true;
+ } else {
+ return Resolve(current_dir, current_toolchain,
+ original_value, toolchain_piece,
+ out_toolchain_dir, out_toolchain_name, NULL, NULL, err);
+ }
+ }
+ return true;
+}
+
+} // namespace
+
+Label::Label() {
+}
+
+Label::Label(const SourceDir& dir,
+ const base::StringPiece& name,
+ const SourceDir& toolchain_dir,
+ const base::StringPiece& toolchain_name)
+ : dir_(dir),
+ toolchain_dir_(toolchain_dir) {
+ name_.assign(name.data(), name.size());
+ toolchain_name_.assign(toolchain_name.data(), toolchain_name.size());
+}
+
+Label::~Label() {
+}
+
+// static
+Label Label::Resolve(const SourceDir& current_dir,
+ const Label& current_toolchain,
+ const Value& input,
+ Err* err) {
+ Label ret;
+ if (input.type() != Value::STRING) {
+ *err = Err(input, "Dependency is not a string.");
+ return ret;
+ }
+ const std::string& input_string = input.string_value();
+ if (input_string.empty()) {
+ *err = Err(input, "Dependency string is empty.");
+ return ret;
+ }
+
+ if (!::Resolve(current_dir, current_toolchain, input, input_string,
+ &ret.dir_, &ret.name_,
+ &ret.toolchain_dir_, &ret.toolchain_name_,
+ err))
+ return Label();
+ return ret;
+}
+
+Label Label::GetToolchainLabel() const {
+ return Label(toolchain_dir_, toolchain_name_,
+ SourceDir(), base::StringPiece());
+}
+
+std::string Label::GetUserVisibleName(bool include_toolchain) const {
+ std::string ret;
+ ret.reserve(dir_.value().size() + name_.size() + 1);
+
+ if (dir_.is_null())
+ return ret;
+
+ ret = DirWithNoTrailingSlash(dir_);
+ ret.push_back(':');
+ ret.append(name_);
+
+ if (include_toolchain) {
+ ret.push_back('(');
+ if (!toolchain_dir_.is_null() && !toolchain_name_.empty()) {
+ ret.append(DirWithNoTrailingSlash(toolchain_dir_));
+ ret.push_back(':');
+ ret.append(toolchain_name_);
+ }
+ ret.push_back(')');
+ }
+ return ret;
+}
+
+std::string Label::GetUserVisibleName(const Label& default_toolchain) const {
+ bool include_toolchain =
+ default_toolchain.dir() != toolchain_dir_ ||
+ default_toolchain.name() != toolchain_name_;
+ return GetUserVisibleName(include_toolchain);
+}
« no previous file with comments | « tools/gn/label.h ('k') | tools/gn/label_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698