Rewrite apps::SavedDevicesService as extensions::DevicePermissionsManager.
[chromium-blink-merge.git] / tools / gn / command_args.cc
blob8b1478fc8883fa733658e10d6946eec283454ec3
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.
5 #include <stdio.h>
6 #include <stdlib.h>
8 #include <map>
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"
25 #if defined(OS_WIN)
26 #include <windows.h>
27 #include <shellapi.h>
28 #endif
30 namespace commands {
32 namespace {
34 const char kSwitchList[] = "list";
35 const char kSwitchShort[] = "short";
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++;
43 return i < line.size() && line[i] == '#';
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;
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;
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();
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;
81 *location_str = file->name().value() + ":" +
82 base::IntToString(location.line_number());
84 const std::string& data = file->contents();
85 size_t line_off =
86 Tokenizer::ByteOffsetOfNthLine(data, location.line_number());
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);
92 base::StringPiece line(&data[previous_line_offset],
93 line_off - previous_line_offset + 1);
94 if (!DoesLineBeginWithComment(line))
95 break;
97 comment->insert(0, StripHashFromLine(line) + "\n");
98 line_off = previous_line_offset;
102 void PrintArgHelp(const base::StringPiece& name, const Value& value) {
103 OutputString(name.as_string(), DECORATION_YELLOW);
104 OutputString(" Default = " + value.ToString(true) + "\n");
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");
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;
121 Scope::KeyValueMap build_args;
122 setup->build_settings().build_args().MergeDeclaredArguments(&build_args);
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 (Scope::KeyValueMap::const_iterator i = build_args.begin();
132 i != build_args.end(); ++i)
133 sorted_args.insert(*i);
134 } else {
135 // List just the one specified as the parameter to --list.
136 Scope::KeyValueMap::const_iterator found_arg = build_args.find(list_value);
137 if (found_arg == build_args.end()) {
138 Err(Location(), "Unknown build argument.",
139 "You asked for \"" + list_value + "\" which I didn't find in any "
140 "build file\nassociated with this build.").PrintToStdout();
141 return 1;
143 sorted_args.insert(*found_arg);
146 if (base::CommandLine::ForCurrentProcess()->HasSwitch(kSwitchShort)) {
147 // Short key=value output.
148 for (std::map<base::StringPiece, Value>::iterator i = sorted_args.begin();
149 i != sorted_args.end(); ++i) {
150 OutputString(i->first.as_string());
151 OutputString(" = ");
152 OutputString(i->second.ToString(true));
153 OutputString("\n");
155 return 0;
158 // Long output.
159 for (std::map<base::StringPiece, Value>::iterator i = sorted_args.begin();
160 i != sorted_args.end(); ++i) {
161 PrintArgHelp(i->first, i->second);
162 OutputString("\n");
165 return 0;
168 #if defined(OS_WIN)
170 bool RunEditor(const base::FilePath& file_to_edit) {
171 SHELLEXECUTEINFO info;
172 memset(&info, 0, sizeof(info));
173 info.cbSize = sizeof(info);
174 info.fMask = SEE_MASK_NOCLOSEPROCESS | SEE_MASK_CLASSNAME;
175 info.lpFile = file_to_edit.value().c_str();
176 info.nShow = SW_SHOW;
177 info.lpClass = L".txt";
178 if (!::ShellExecuteEx(&info)) {
179 Err(Location(), "Couldn't run editor.",
180 "Just edit \"" + FilePathToUTF8(file_to_edit) +
181 "\" manually instead.").PrintToStdout();
182 return false;
185 if (!info.hProcess) {
186 // Windows re-used an existing process.
187 OutputString("\"" + FilePathToUTF8(file_to_edit) +
188 "\" opened in editor, save it and press <Enter> when done.\n");
189 getchar();
190 } else {
191 OutputString("Waiting for editor on \"" + FilePathToUTF8(file_to_edit) +
192 "\"...\n");
193 ::WaitForSingleObject(info.hProcess, INFINITE);
194 ::CloseHandle(info.hProcess);
196 return true;
199 #else // POSIX
201 bool RunEditor(const base::FilePath& file_to_edit) {
202 // Prefer $VISUAL, then $EDITOR, then vi.
203 const char* editor_ptr = getenv("VISUAL");
204 if (!editor_ptr)
205 editor_ptr = getenv("EDITOR");
206 if (!editor_ptr)
207 editor_ptr = "vi";
209 std::string cmd(editor_ptr);
210 cmd.append(" \"");
212 // Its impossible to do this properly since we don't know the user's shell,
213 // but quoting and escaping internal quotes should handle 99.999% of all
214 // cases.
215 std::string escaped_name = file_to_edit.value();
216 ReplaceSubstringsAfterOffset(&escaped_name, 0, "\"", "\\\"");
217 cmd.append(escaped_name);
218 cmd.push_back('"');
220 OutputString("Waiting for editor on \"" + file_to_edit.value() +
221 "\"...\n");
222 return system(cmd.c_str()) == 0;
225 #endif
227 int EditArgsFile(const std::string& build_dir) {
229 // Scope the setup. We only use it for some basic state. We'll do the
230 // "real" build below in the gen command.
231 Setup setup;
232 setup.set_check_for_bad_items(false);
233 // Don't fill build arguments. We're about to edit the file which supplies
234 // these in the first place.
235 setup.set_fill_arguments(false);
236 if (!setup.DoSetup(build_dir, true))
237 return 1;
239 // Ensure the file exists. Need to normalize path separators since on
240 // Windows they can come out as forward slashes here, and that confuses some
241 // of the commands.
242 base::FilePath arg_file =
243 setup.build_settings().GetFullPath(setup.GetBuildArgFile())
244 .NormalizePathSeparators();
245 if (!base::PathExists(arg_file)) {
246 std::string argfile_default_contents =
247 "# Build arguments go here. Examples:\n"
248 "# enable_doom_melon = true\n"
249 "# crazy_something = \"absolutely\"\n";
250 #if defined(OS_WIN)
251 // Use Windows lineendings for this file since it will often open in
252 // Notepad which can't handle Unix ones.
253 ReplaceSubstringsAfterOffset(&argfile_default_contents, 0, "\n", "\r\n");
254 #endif
255 base::CreateDirectory(arg_file.DirName());
256 base::WriteFile(arg_file, argfile_default_contents.c_str(),
257 static_cast<int>(argfile_default_contents.size()));
260 ScopedTrace editor_trace(TraceItem::TRACE_SETUP, "Waiting for editor");
261 if (!RunEditor(arg_file))
262 return 1;
265 // Now do a normal "gen" command.
266 OutputString("Generating files...\n");
267 std::vector<std::string> gen_commands;
268 gen_commands.push_back(build_dir);
269 return RunGen(gen_commands);
272 } // namespace
274 extern const char kArgs[] = "args";
275 extern const char kArgs_HelpShort[] =
276 "args: Display or configure arguments declared by the build.";
277 extern const char kArgs_Help[] =
278 "gn args [arg name]\n"
279 "\n"
280 " See also \"gn help buildargs\" for a more high-level overview of how\n"
281 " build arguments work.\n"
282 "\n"
283 "Usage\n"
284 " gn args <dir_name>\n"
285 " Open the arguments for the given build directory in an editor\n"
286 " (as specified by the EDITOR environment variable). If the given\n"
287 " build directory doesn't exist, it will be created and an empty\n"
288 " args file will be opened in the editor. You would type something\n"
289 " like this into that file:\n"
290 " enable_doom_melon=false\n"
291 " os=\"android\"\n"
292 "\n"
293 " Note: you can edit the build args manually by editing the file\n"
294 " \"args.gn\" in the build directory and then running\n"
295 " \"gn gen <build_dir>\".\n"
296 "\n"
297 " gn args <dir_name> --list[=<exact_arg>] [--short]\n"
298 " Lists all build arguments available in the current configuration,\n"
299 " or, if an exact_arg is specified for the list flag, just that one\n"
300 " build argument.\n"
301 "\n"
302 " The output will list the declaration location, default value, and\n"
303 " comment preceeding the declaration. If --short is specified,\n"
304 " only the names and values will be printed.\n"
305 "\n"
306 " If the dir_name is specified, the build configuration will be\n"
307 " taken from that build directory. The reason this is needed is that\n"
308 " the definition of some arguments is dependent on the build\n"
309 " configuration, so setting some values might add, remove, or change\n"
310 " the default values for other arguments. Specifying your exact\n"
311 " configuration allows the proper arguments to be displayed.\n"
312 "\n"
313 " Instead of specifying the dir_name, you can also use the\n"
314 " command-line flag to specify the build configuration:\n"
315 " --args=<exact list of args to use>\n"
316 "\n"
317 "Examples\n"
318 " gn args out/Debug\n"
319 " Opens an editor with the args for out/Debug.\n"
320 "\n"
321 " gn args out/Debug --list --short\n"
322 " Prints all arguments with their default values for the out/Debug\n"
323 " build.\n"
324 "\n"
325 " gn args out/Debug --list=cpu_arch\n"
326 " Prints information about the \"cpu_arch\" argument for the out/Debug\n"
327 " build.\n"
328 "\n"
329 " gn args --list --args=\"os=\\\"android\\\" enable_doom_melon=true\"\n"
330 " Prints all arguments with the default values for a build with the\n"
331 " given arguments set (which may affect the values of other\n"
332 " arguments).\n";
334 int RunArgs(const std::vector<std::string>& args) {
335 if (args.size() != 1) {
336 Err(Location(), "Exactly one build dir needed.",
337 "Usage: \"gn args <build_dir>\"\n"
338 "Or see \"gn help args\" for more variants.").PrintToStdout();
339 return 1;
342 if (base::CommandLine::ForCurrentProcess()->HasSwitch(kSwitchList))
343 return ListArgs(args[0]);
344 return EditArgsFile(args[0]);
347 } // namespace commands