Home | History | Annotate | Download | only in gn
      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