Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / tools / gn / command_args.cc
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 <stdio.h>
6 #include <stdlib.h>
7
8 #include <map>
9
10 #include "base/command_line.h"
11 #include "base/environment.h"
12 #include "base/files/file_util.h"
13 #include "base/process/launch.h"
14 #include "base/strings/string_number_conversions.h"
15 #include "base/strings/string_util.h"
16 #include "tools/gn/commands.h"
17 #include "tools/gn/filesystem_utils.h"
18 #include "tools/gn/input_file.h"
19 #include "tools/gn/parse_tree.h"
20 #include "tools/gn/setup.h"
21 #include "tools/gn/standard_out.h"
22 #include "tools/gn/tokenizer.h"
23 #include "tools/gn/trace.h"
24
25 #if defined(OS_WIN)
26 #include <windows.h>
27 #include <shellapi.h>
28 #endif
29
30 namespace commands {
31
32 namespace {
33
34 const char kSwitchList[] = "list";
35 const char kSwitchShort[] = "short";
36
37 bool DoesLineBeginWithComment(const base::StringPiece& line) {
38   // Skip whitespace.
39   size_t i = 0;
40   while (i < line.size() && IsAsciiWhitespace(line[i]))
41     i++;
42
43   return i < line.size() && line[i] == '#';
44 }
45
46 // Returns the offset of the beginning of the line identified by |offset|.
47 size_t BackUpToLineBegin(const std::string& data, size_t offset) {
48   // Degenerate case of an empty line. Below we'll try to return the
49   // character after the newline, but that will be incorrect in this case.
50   if (offset == 0 || Tokenizer::IsNewline(data, offset))
51     return offset;
52
53   size_t cur = offset;
54   do {
55     cur --;
56     if (Tokenizer::IsNewline(data, cur))
57       return cur + 1;  // Want the first character *after* the newline.
58   } while (cur > 0);
59   return 0;
60 }
61
62 // Assumes DoesLineBeginWithComment(), this strips the # character from the
63 // beginning and normalizes preceeding whitespace.
64 std::string StripHashFromLine(const base::StringPiece& line) {
65   // Replace the # sign and everything before it with 3 spaces, so that a
66   // normal comment that has a space after the # will be indented 4 spaces
67   // (which makes our formatting come out nicely). If the comment is indented
68   // from there, we want to preserve that indenting.
69   return "   " + line.substr(line.find('#') + 1).as_string();
70 }
71
72 // Tries to find the comment before the setting of the given value.
73 void GetContextForValue(const Value& value,
74                         std::string* location_str,
75                         std::string* comment) {
76   Location location = value.origin()->GetRange().begin();
77   const InputFile* file = location.file();
78   if (!file)
79     return;
80
81   *location_str = file->name().value() + ":" +
82       base::IntToString(location.line_number());
83
84   const std::string& data = file->contents();
85   size_t line_off =
86       Tokenizer::ByteOffsetOfNthLine(data, location.line_number());
87
88   while (line_off > 1) {
89     line_off -= 2;  // Back up to end of previous line.
90     size_t previous_line_offset = BackUpToLineBegin(data, line_off);
91
92     base::StringPiece line(&data[previous_line_offset],
93                            line_off - previous_line_offset + 1);
94     if (!DoesLineBeginWithComment(line))
95       break;
96
97     comment->insert(0, StripHashFromLine(line) + "\n");
98     line_off = previous_line_offset;
99   }
100 }
101
102 void PrintArgHelp(const base::StringPiece& name, const Value& value) {
103   OutputString(name.as_string(), DECORATION_YELLOW);
104   OutputString("  Default = " + value.ToString(true) + "\n");
105
106   if (value.origin()) {
107     std::string location, comment;
108     GetContextForValue(value, &location, &comment);
109     OutputString("    " + location + "\n" + comment);
110   } else {
111     OutputString("    (Internally set)\n");
112   }
113 }
114
115 int ListArgs(const std::string& build_dir) {
116   Setup* setup = new Setup;
117   setup->set_check_for_bad_items(false);
118   if (!setup->DoSetup(build_dir, false) || !setup->Run())
119     return 1;
120
121   Scope::KeyValueMap build_args;
122   setup->build_settings().build_args().MergeDeclaredArguments(&build_args);
123
124   // Find all of the arguments we care about. Use a regular map so they're
125   // sorted nicely when we write them out.
126   std::map<base::StringPiece, Value> sorted_args;
127   std::string list_value =
128       base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(kSwitchList);
129   if (list_value.empty()) {
130     // List all values.
131     for (const auto& arg : build_args)
132       sorted_args.insert(arg);
133   } else {
134     // List just the one specified as the parameter to --list.
135     Scope::KeyValueMap::const_iterator found_arg = build_args.find(list_value);
136     if (found_arg == build_args.end()) {
137       Err(Location(), "Unknown build argument.",
138           "You asked for \"" + list_value + "\" which I didn't find in any "
139           "build file\nassociated with this build.").PrintToStdout();
140       return 1;
141     }
142     sorted_args.insert(*found_arg);
143   }
144
145   if (base::CommandLine::ForCurrentProcess()->HasSwitch(kSwitchShort)) {
146     // Short key=value output.
147     for (const auto& arg : sorted_args) {
148       OutputString(arg.first.as_string());
149       OutputString(" = ");
150       OutputString(arg.second.ToString(true));
151       OutputString("\n");
152     }
153     return 0;
154   }
155
156   // Long output.
157   for (const auto& arg : sorted_args) {
158     PrintArgHelp(arg.first, arg.second);
159     OutputString("\n");
160   }
161
162   return 0;
163 }
164
165 #if defined(OS_WIN)
166
167 bool RunEditor(const base::FilePath& file_to_edit) {
168   SHELLEXECUTEINFO info;
169   memset(&info, 0, sizeof(info));
170   info.cbSize = sizeof(info);
171   info.fMask = SEE_MASK_NOCLOSEPROCESS | SEE_MASK_CLASSNAME;
172   info.lpFile = file_to_edit.value().c_str();
173   info.nShow = SW_SHOW;
174   info.lpClass = L".txt";
175   if (!::ShellExecuteEx(&info)) {
176     Err(Location(), "Couldn't run editor.",
177         "Just edit \"" + FilePathToUTF8(file_to_edit) +
178         "\" manually instead.").PrintToStdout();
179     return false;
180   }
181
182   if (!info.hProcess) {
183     // Windows re-used an existing process.
184     OutputString("\"" + FilePathToUTF8(file_to_edit) +
185                  "\" opened in editor, save it and press <Enter> when done.\n");
186     getchar();
187   } else {
188     OutputString("Waiting for editor on \"" + FilePathToUTF8(file_to_edit) +
189                  "\"...\n");
190     ::WaitForSingleObject(info.hProcess, INFINITE);
191     ::CloseHandle(info.hProcess);
192   }
193   return true;
194 }
195
196 #else  // POSIX
197
198 bool RunEditor(const base::FilePath& file_to_edit) {
199   const char* editor_ptr = getenv("VISUAL");
200   if (!editor_ptr)
201     editor_ptr = getenv("GN_EDITOR");
202   if (!editor_ptr)
203     editor_ptr = getenv("EDITOR");
204   if (!editor_ptr)
205     editor_ptr = "vi";
206
207   std::string cmd(editor_ptr);
208   cmd.append(" \"");
209
210   // Its impossible to do this properly since we don't know the user's shell,
211   // but quoting and escaping internal quotes should handle 99.999% of all
212   // cases.
213   std::string escaped_name = file_to_edit.value();
214   ReplaceSubstringsAfterOffset(&escaped_name, 0, "\"", "\\\"");
215   cmd.append(escaped_name);
216   cmd.push_back('"');
217
218   OutputString("Waiting for editor on \"" + file_to_edit.value() +
219                "\"...\n");
220   return system(cmd.c_str()) == 0;
221 }
222
223 #endif
224
225 int EditArgsFile(const std::string& build_dir) {
226   {
227     // Scope the setup. We only use it for some basic state. We'll do the
228     // "real" build below in the gen command.
229     Setup setup;
230     setup.set_check_for_bad_items(false);
231     // Don't fill build arguments. We're about to edit the file which supplies
232     // these in the first place.
233     setup.set_fill_arguments(false);
234     if (!setup.DoSetup(build_dir, true))
235       return 1;
236
237     // Ensure the file exists. Need to normalize path separators since on
238     // Windows they can come out as forward slashes here, and that confuses some
239     // of the commands.
240     base::FilePath arg_file =
241         setup.build_settings().GetFullPath(setup.GetBuildArgFile())
242         .NormalizePathSeparators();
243     if (!base::PathExists(arg_file)) {
244       std::string argfile_default_contents =
245           "# Build arguments go here. Examples:\n"
246           "#   enable_doom_melon = true\n"
247           "#   crazy_something = \"absolutely\"\n";
248 #if defined(OS_WIN)
249       // Use Windows lineendings for this file since it will often open in
250       // Notepad which can't handle Unix ones.
251       ReplaceSubstringsAfterOffset(&argfile_default_contents, 0, "\n", "\r\n");
252 #endif
253       base::CreateDirectory(arg_file.DirName());
254       base::WriteFile(arg_file, argfile_default_contents.c_str(),
255                       static_cast<int>(argfile_default_contents.size()));
256     }
257
258     ScopedTrace editor_trace(TraceItem::TRACE_SETUP, "Waiting for editor");
259     if (!RunEditor(arg_file))
260       return 1;
261   }
262
263   // Now do a normal "gen" command.
264   OutputString("Generating files...\n");
265   std::vector<std::string> gen_commands;
266   gen_commands.push_back(build_dir);
267   return RunGen(gen_commands);
268 }
269
270 }  // namespace
271
272 extern const char kArgs[] = "args";
273 extern const char kArgs_HelpShort[] =
274     "args: Display or configure arguments declared by the build.";
275 extern const char kArgs_Help[] =
276     "gn args [arg name]\n"
277     "\n"
278     "  See also \"gn help buildargs\" for a more high-level overview of how\n"
279     "  build arguments work.\n"
280     "\n"
281     "Usage\n"
282     "  gn args <dir_name>\n"
283     "      Open the arguments for the given build directory in an editor\n"
284     "      (as specified by the EDITOR environment variable). If the given\n"
285     "      build directory doesn't exist, it will be created and an empty\n"
286     "      args file will be opened in the editor. You would type something\n"
287     "      like this into that file:\n"
288     "          enable_doom_melon=false\n"
289     "          os=\"android\"\n"
290     "\n"
291     "      Note: you can edit the build args manually by editing the file\n"
292     "      \"args.gn\" in the build directory and then running\n"
293     "      \"gn gen <build_dir>\".\n"
294     "\n"
295     "  gn args <dir_name> --list[=<exact_arg>] [--short]\n"
296     "      Lists all build arguments available in the current configuration,\n"
297     "      or, if an exact_arg is specified for the list flag, just that one\n"
298     "      build argument.\n"
299     "\n"
300     "      The output will list the declaration location, default value, and\n"
301     "      comment preceeding the declaration. If --short is specified,\n"
302     "      only the names and values will be printed.\n"
303     "\n"
304     "      If the dir_name is specified, the build configuration will be\n"
305     "      taken from that build directory. The reason this is needed is that\n"
306     "      the definition of some arguments is dependent on the build\n"
307     "      configuration, so setting some values might add, remove, or change\n"
308     "      the default values for other arguments. Specifying your exact\n"
309     "      configuration allows the proper arguments to be displayed.\n"
310     "\n"
311     "      Instead of specifying the dir_name, you can also use the\n"
312     "      command-line flag to specify the build configuration:\n"
313     "        --args=<exact list of args to use>\n"
314     "\n"
315     "Examples\n"
316     "  gn args out/Debug\n"
317     "    Opens an editor with the args for out/Debug.\n"
318     "\n"
319     "  gn args out/Debug --list --short\n"
320     "    Prints all arguments with their default values for the out/Debug\n"
321     "    build.\n"
322     "\n"
323     "  gn args out/Debug --list=cpu_arch\n"
324     "    Prints information about the \"cpu_arch\" argument for the out/Debug\n"
325     "    build.\n"
326     "\n"
327     "  gn args --list --args=\"os=\\\"android\\\" enable_doom_melon=true\"\n"
328     "    Prints all arguments with the default values for a build with the\n"
329     "    given arguments set (which may affect the values of other\n"
330     "    arguments).\n";
331
332 int RunArgs(const std::vector<std::string>& args) {
333   if (args.size() != 1) {
334     Err(Location(), "Exactly one build dir needed.",
335         "Usage: \"gn args <build_dir>\"\n"
336         "Or see \"gn help args\" for more variants.").PrintToStdout();
337     return 1;
338   }
339
340   if (base::CommandLine::ForCurrentProcess()->HasSwitch(kSwitchList))
341     return ListArgs(args[0]);
342   return EditArgsFile(args[0]);
343 }
344
345 }  // namespace commands