1 // Copyright (c) 2013 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. 4 5 #include <map> 6 7 #include "base/strings/string_number_conversions.h" 8 #include "base/strings/string_util.h" 9 #include "tools/gn/commands.h" 10 #include "tools/gn/input_file.h" 11 #include "tools/gn/parse_tree.h" 12 #include "tools/gn/setup.h" 13 #include "tools/gn/standard_out.h" 14 #include "tools/gn/tokenizer.h" 15 16 namespace commands { 17 18 namespace { 19 20 bool DoesLineBeginWithComment(const base::StringPiece& line) { 21 // Skip whitespace. 22 size_t i = 0; 23 while (i < line.size() && IsAsciiWhitespace(line[i])) 24 i++; 25 26 return i < line.size() && line[i] == '#'; 27 } 28 29 // Returns the offset of the beginning of the line identified by |offset|. 30 size_t BackUpToLineBegin(const std::string& data, size_t offset) { 31 // Degenerate case of an empty line. Below we'll try to return the 32 // character after the newline, but that will be incorrect in this case. 33 if (offset == 0 || Tokenizer::IsNewline(data, offset)) 34 return offset; 35 36 size_t cur = offset; 37 do { 38 cur --; 39 if (Tokenizer::IsNewline(data, cur)) 40 return cur + 1; // Want the first character *after* the newline. 41 } while (cur > 0); 42 return 0; 43 } 44 45 // Assumes DoesLineBeginWithComment(). 46 std::string StripCommentFromLine(const base::StringPiece& line) { 47 std::string ret = line.as_string(); 48 for (size_t i = 0; i < ret.size(); i++) { 49 if (ret[i] == '#') { 50 ret[i] = ' '; 51 break; 52 } 53 } 54 return ret; 55 } 56 57 // Tries to find the comment before the setting of the given value. 58 void GetContextForValue(const Value& value, 59 std::string* location_str, 60 std::string* comment) { 61 Location location = value.origin()->GetRange().begin(); 62 const InputFile* file = location.file(); 63 if (!file) 64 return; 65 66 *location_str = file->name().value() + ":" + 67 base::IntToString(location.line_number()); 68 69 const std::string& data = file->contents(); 70 size_t line_off = 71 Tokenizer::ByteOffsetOfNthLine(data, location.line_number()); 72 73 while (line_off > 1) { 74 line_off -= 2; // Back up to end of previous line. 75 size_t previous_line_offset = BackUpToLineBegin(data, line_off); 76 77 base::StringPiece line(&data[previous_line_offset], 78 line_off - previous_line_offset + 1); 79 if (!DoesLineBeginWithComment(line)) 80 break; 81 82 comment->insert(0, StripCommentFromLine(line) + "\n"); 83 line_off = previous_line_offset; 84 } 85 } 86 87 void PrintArgHelp(const base::StringPiece& name, const Value& value) { 88 OutputString(name.as_string(), DECORATION_YELLOW); 89 OutputString(" Default = " + value.ToString(true) + "\n"); 90 91 if (value.origin()) { 92 std::string location, comment; 93 GetContextForValue(value, &location, &comment); 94 OutputString(" " + location + "\n" + comment); 95 } else { 96 OutputString(" (Internally set)\n"); 97 } 98 } 99 100 } // namespace 101 102 extern const char kArgs[] = "args"; 103 extern const char kArgs_HelpShort[] = 104 "args: Display configurable arguments declared by the build."; 105 extern const char kArgs_Help[] = 106 "gn args [arg name]\n" 107 " Displays all arguments declared by buildfiles along with their\n" 108 " description. Build arguments are anything in a declare_args() block\n" 109 " in any buildfile. The comment preceeding the declaration will be\n" 110 " displayed here (so comment well!).\n" 111 "\n" 112 " These arguments can be overriden on the command-line:\n" 113 " --args=\"doom_melon_setting=5 component_build=1\"\n" 114 " or in a toolchain definition (see \"gn help buildargs\" for more on\n" 115 " how this all works).\n" 116 "\n" 117 " If \"arg name\" is specified, only the information for that argument\n" 118 " will be displayed. Otherwise all arguments will be displayed.\n"; 119 120 int RunArgs(const std::vector<std::string>& args) { 121 Setup* setup = new Setup; 122 setup->set_check_for_bad_items(false); 123 if (!setup->DoSetup() || !setup->Run()) 124 return 1; 125 126 const Scope::KeyValueMap& build_args = 127 setup->build_settings().build_args().declared_arguments(); 128 129 if (args.size() == 1) { 130 // Get help on a specific command. 131 Scope::KeyValueMap::const_iterator found_arg = build_args.find(args[0]); 132 if (found_arg == build_args.end()) { 133 Err(Location(), "Unknown build arg.", 134 "You asked for \"" + args[0] + "\" which I didn't find in any " 135 "buildfile\nassociated with this build."); 136 return 1; 137 } 138 PrintArgHelp(args[0], found_arg->second); 139 return 0; 140 } else if (args.size() > 1) { 141 // Too many arguments. 142 Err(Location(), "You're holding it wrong.", 143 "Usage: \"gn args [arg name]\"").PrintToStdout(); 144 return 1; 145 } 146 147 // List all arguments. First put them in a regular map so they're sorted. 148 std::map<base::StringPiece, Value> sorted_args; 149 for (Scope::KeyValueMap::const_iterator i = build_args.begin(); 150 i != build_args.end(); ++i) 151 sorted_args.insert(*i); 152 153 for (std::map<base::StringPiece, Value>::iterator i = sorted_args.begin(); 154 i != sorted_args.end(); ++i) { 155 PrintArgHelp(i->first, i->second); 156 OutputString("\n"); 157 } 158 159 return 0; 160 } 161 162 } // namespace commands 163