Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include <stdio.h> | |
| 6 #include <stdlib.h> | |
| 7 | |
| 5 #include <map> | 8 #include <map> |
| 6 | 9 |
| 10 #include "base/command_line.h" | |
| 11 #include "base/environment.h" | |
| 12 #include "base/file_util.h" | |
| 13 #include "base/process/launch.h" | |
| 7 #include "base/strings/string_number_conversions.h" | 14 #include "base/strings/string_number_conversions.h" |
| 8 #include "base/strings/string_util.h" | 15 #include "base/strings/string_util.h" |
| 9 #include "tools/gn/commands.h" | 16 #include "tools/gn/commands.h" |
| 17 #include "tools/gn/filesystem_utils.h" | |
| 10 #include "tools/gn/input_file.h" | 18 #include "tools/gn/input_file.h" |
| 11 #include "tools/gn/parse_tree.h" | 19 #include "tools/gn/parse_tree.h" |
| 12 #include "tools/gn/setup.h" | 20 #include "tools/gn/setup.h" |
| 13 #include "tools/gn/standard_out.h" | 21 #include "tools/gn/standard_out.h" |
| 14 #include "tools/gn/tokenizer.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 | |
| 15 | 29 |
| 16 namespace commands { | 30 namespace commands { |
| 17 | 31 |
| 18 namespace { | 32 namespace { |
| 19 | 33 |
| 34 const char kSwitchList[] = "list"; | |
| 35 const char kSwitchShort[] = "short"; | |
| 36 | |
| 20 bool DoesLineBeginWithComment(const base::StringPiece& line) { | 37 bool DoesLineBeginWithComment(const base::StringPiece& line) { |
| 21 // Skip whitespace. | 38 // Skip whitespace. |
| 22 size_t i = 0; | 39 size_t i = 0; |
| 23 while (i < line.size() && IsAsciiWhitespace(line[i])) | 40 while (i < line.size() && IsAsciiWhitespace(line[i])) |
| 24 i++; | 41 i++; |
| 25 | 42 |
| 26 return i < line.size() && line[i] == '#'; | 43 return i < line.size() && line[i] == '#'; |
| 27 } | 44 } |
| 28 | 45 |
| 29 // Returns the offset of the beginning of the line identified by |offset|. | 46 // Returns the offset of the beginning of the line identified by |offset|. |
| (...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 88 | 105 |
| 89 if (value.origin()) { | 106 if (value.origin()) { |
| 90 std::string location, comment; | 107 std::string location, comment; |
| 91 GetContextForValue(value, &location, &comment); | 108 GetContextForValue(value, &location, &comment); |
| 92 OutputString(" " + location + "\n" + comment); | 109 OutputString(" " + location + "\n" + comment); |
| 93 } else { | 110 } else { |
| 94 OutputString(" (Internally set)\n"); | 111 OutputString(" (Internally set)\n"); |
| 95 } | 112 } |
| 96 } | 113 } |
| 97 | 114 |
| 98 } // namespace | 115 int ListArgs(const std::string& build_dir) { |
| 99 | |
| 100 extern const char kArgs[] = "args"; | |
| 101 extern const char kArgs_HelpShort[] = | |
| 102 "args: Display configurable arguments declared by the build."; | |
| 103 extern const char kArgs_Help[] = | |
| 104 "gn args [arg name]\n" | |
| 105 " Displays all arguments declared by buildfiles along with their\n" | |
| 106 " description. Build arguments are anything in a declare_args() block\n" | |
| 107 " in any buildfile. The comment preceding the declaration will be\n" | |
| 108 " displayed here (so comment well!).\n" | |
| 109 "\n" | |
| 110 " These arguments can be overridden on the command-line:\n" | |
| 111 " --args=\"doom_melon_setting=5 component_build=1\"\n" | |
| 112 " or in a toolchain definition (see \"gn help buildargs\" for more on\n" | |
| 113 " how this all works).\n" | |
| 114 "\n" | |
| 115 " If \"arg name\" is specified, only the information for that argument\n" | |
| 116 " will be displayed. Otherwise all arguments will be displayed.\n"; | |
| 117 | |
| 118 int RunArgs(const std::vector<std::string>& args) { | |
| 119 Setup* setup = new Setup; | 116 Setup* setup = new Setup; |
| 120 setup->set_check_for_bad_items(false); | 117 setup->set_check_for_bad_items(false); |
| 121 // TODO(brettw) bug 343726: Use a temporary directory instead of this | 118 if (!setup->DoSetup(build_dir) || !setup->Run()) |
| 122 // default one to avoid messing up any build that's in there. | |
| 123 if (!setup->DoSetup("//out/Default/") || !setup->Run()) | |
| 124 return 1; | 119 return 1; |
| 125 | 120 |
| 126 Scope::KeyValueMap build_args; | 121 Scope::KeyValueMap build_args; |
| 127 setup->build_settings().build_args().MergeDeclaredArguments(&build_args); | 122 setup->build_settings().build_args().MergeDeclaredArguments(&build_args); |
| 128 | 123 |
| 129 if (args.size() == 1) { | 124 // Find all of the arguments we care about. Use a regular map so they're |
| 130 // Get help on a specific command. | 125 // sorted nicely when we write them out. |
| 131 Scope::KeyValueMap::const_iterator found_arg = build_args.find(args[0]); | 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); | |
| 132 if (found_arg == build_args.end()) { | 137 if (found_arg == build_args.end()) { |
| 133 Err(Location(), "Unknown build argument.", | 138 Err(Location(), "Unknown build argument.", |
| 134 "You asked for \"" + args[0] + "\" which I didn't find in any " | 139 "You asked for \"" + list_value + "\" which I didn't find in any " |
| 135 "buildfile\nassociated with this build."); | 140 "build file\nassociated with this build.").PrintToStdout(); |
| 136 return 1; | 141 return 1; |
| 137 } | 142 } |
| 138 PrintArgHelp(args[0], found_arg->second); | 143 sorted_args.insert(*found_arg); |
| 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 } | 144 } |
| 146 | 145 |
| 147 // List all arguments. First put them in a regular map so they're sorted. | 146 if (base::CommandLine::ForCurrentProcess()->HasSwitch(kSwitchShort)) { |
| 148 std::map<base::StringPiece, Value> sorted_args; | 147 // Short key=value output. |
| 149 for (Scope::KeyValueMap::const_iterator i = build_args.begin(); | 148 for (std::map<base::StringPiece, Value>::iterator i = sorted_args.begin(); |
| 150 i != build_args.end(); ++i) | 149 i != sorted_args.end(); ++i) { |
| 151 sorted_args.insert(*i); | 150 OutputString(i->first.as_string()); |
| 151 OutputString(" = "); | |
| 152 OutputString(i->second.ToString(true)); | |
| 153 OutputString("\n"); | |
| 154 } | |
| 155 return 0; | |
| 156 } | |
| 152 | 157 |
| 153 OutputString( | 158 // Long output. |
| 154 "Available build arguments. Note that the which arguments are declared\n" | |
| 155 "and their default values may depend on other arguments or the current\n" | |
| 156 "platform and architecture. So setting some values may add, remove, or\n" | |
| 157 "change the default value of other values.\n\n"); | |
| 158 | |
| 159 for (std::map<base::StringPiece, Value>::iterator i = sorted_args.begin(); | 159 for (std::map<base::StringPiece, Value>::iterator i = sorted_args.begin(); |
| 160 i != sorted_args.end(); ++i) { | 160 i != sorted_args.end(); ++i) { |
| 161 PrintArgHelp(i->first, i->second); | 161 PrintArgHelp(i->first, i->second); |
| 162 OutputString("\n"); | 162 OutputString("\n"); |
| 163 } | 163 } |
| 164 | 164 |
| 165 return 0; | 165 return 0; |
| 166 } | 166 } |
| 167 | 167 |
| 168 #if defined(OS_WIN) | |
| 169 | |
| 170 void 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; | |
|
scottmg
2014/05/01 19:25:02
This should probably exit(1) instead otherwise the
brettw
2014/05/01 19:46:55
I added proper error checking and return values to
| |
| 183 } | |
| 184 | |
| 185 if (!info.hProcess) { | |
| 186 // Windows re-used an existing process. | |
| 187 OutputString( | |
| 188 "File opened in editor, save it and press <Enter> when done.\n"); | |
| 189 getchar(); | |
| 190 } else { | |
| 191 OutputString("Waiting for editor...\n"); | |
| 192 ::WaitForSingleObject(info.hProcess, INFINITE); | |
| 193 ::CloseHandle(info.hProcess); | |
| 194 } | |
| 195 } | |
| 196 | |
| 197 #else // POSIX | |
| 198 | |
| 199 void RunEditor(const base::FilePath& file_to_edit) { | |
| 200 // Prefer $VISUAL, then $EDITOR, then vi. | |
| 201 const char* editor_ptr = getenv("VISUAL"); | |
| 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 system(cmd.c_str()); | |
| 219 } | |
| 220 | |
| 221 #endif | |
| 222 | |
| 223 int EditArgsFile(const std::string& build_dir) { | |
| 224 { | |
| 225 // Scope the setup. We only use it for some basic state. We'll do the | |
| 226 // "real" build below in the gen command. | |
| 227 Setup setup; | |
| 228 setup.set_check_for_bad_items(false); | |
| 229 // Don't fill build arguments. We're about to edit the file which supplies | |
| 230 // these in the first place. | |
| 231 setup.set_fill_arguments(false); | |
| 232 if (!setup.DoSetup(build_dir)) | |
| 233 return 1; | |
| 234 | |
| 235 // Ensure the file exists. Need to normalize path separators since on | |
| 236 // Windows they can come out as forward slashes here, and that conuses some | |
|
scottmg
2014/05/01 19:25:02
confuses
| |
| 237 // of the commands. | |
| 238 base::FilePath arg_file = | |
| 239 setup.build_settings().GetFullPath(setup.GetBuildArgFile()) | |
| 240 .NormalizePathSeparators(); | |
| 241 if (!base::PathExists(arg_file)) { | |
| 242 std::string argfile_default_contents = | |
|
scottmg
2014/05/01 19:25:02
It'd be nice to have the default values/comments i
brettw
2014/05/01 19:46:55
There will be many many dozens of arguments, so I'
| |
| 243 "# Build arguments go here. Examples:\n" | |
| 244 "# is_debug = false\n" | |
| 245 "# crazy_something = \"absolutely\"\n\n"; | |
| 246 #if defined(OS_WIN) | |
| 247 // Use Windows lineendings for this file since it will often open in | |
| 248 // Notepad which can't handle Unix ones. | |
| 249 ReplaceSubstringsAfterOffset(&argfile_default_contents, 0, "\n", "\r\n"); | |
| 250 #endif | |
| 251 base::CreateDirectory(arg_file.DirName()); | |
| 252 base::WriteFile(arg_file, argfile_default_contents.c_str(), | |
| 253 argfile_default_contents.size()); | |
| 254 } | |
| 255 | |
| 256 ScopedTrace editor_trace(TraceItem::TRACE_SETUP, "Waiting for editor"); | |
| 257 RunEditor(arg_file); | |
| 258 } | |
| 259 | |
| 260 // Now do a normal "gen" command. | |
| 261 OutputString("Generating files...\n"); | |
| 262 std::vector<std::string> gen_commands; | |
| 263 gen_commands.push_back(build_dir); | |
| 264 return RunGen(gen_commands); | |
| 265 } | |
| 266 | |
| 267 } // namespace | |
| 268 | |
| 269 extern const char kArgs[] = "args"; | |
| 270 extern const char kArgs_HelpShort[] = | |
| 271 "args: Display or configure arguments declared by the build."; | |
| 272 extern const char kArgs_Help[] = | |
| 273 "gn args [arg name]\n" | |
| 274 "\n" | |
| 275 " See also \"gn help buildargs\" for a more high-level overview of how\n" | |
| 276 " build arguments work.\n" | |
| 277 "\n" | |
| 278 "Usage\n" | |
| 279 " gn args <dir_name>\n" | |
| 280 " Open the arguments for the given build directory in an editor\n" | |
| 281 " (as specified by the EDITOR environment variable). If the given\n" | |
| 282 " build directory doesn't exist, it will be created and an empty\n" | |
| 283 " args file will be opened in the editor. You would type something\n" | |
| 284 " like this into that file:\n" | |
| 285 " enable_doom_melon=false\n" | |
| 286 " os=\"android\"\n" | |
| 287 "\n" | |
| 288 " Note: you can edit the build args manually by editing the file\n" | |
| 289 " \"gn.args\" in the build directory and then running\n" | |
| 290 " \"gn gen <build_dir>\".\n" | |
| 291 "\n" | |
| 292 " gn args <dir_name> --list[=<exact_arg>] [--short]\n" | |
| 293 " Lists all build arguments available in the current configuration,\n" | |
| 294 " or, if an exact_arg is specified for the list flag, just that one\n" | |
| 295 " build argument.\n" | |
| 296 "\n" | |
| 297 " The output will list the declaration location, default value, and\n" | |
| 298 " comment preceeding the declaration. If --short is specified,\n" | |
| 299 " only the names and values will be printed.\n" | |
| 300 "\n" | |
| 301 " If the dir_name is specified, the build configuration will be\n" | |
| 302 " taken from that build directory. The reason this is needed is that\n" | |
| 303 " the definition of some arguments is dependent on the build\n" | |
| 304 " configuration, so setting some values might add, remove, or change\n" | |
| 305 " the default values for other arguments. Specifying your exact\n" | |
| 306 " configuration allows the proper arguments to be displayed.\n" | |
| 307 "\n" | |
| 308 " Instead of specifying the dir_name, you can also use the\n" | |
| 309 " command-line flag to specify the build configuration:\n" | |
| 310 " --args=<exact list of args to use>\n" | |
| 311 "\n" | |
| 312 "Examples\n" | |
| 313 " gn args out/Debug\n" | |
| 314 " Opens an editor with the args for out/Debug.\n" | |
| 315 "\n" | |
| 316 " gn args out/Debug --list --short\n" | |
| 317 " Prints all arguments with their default values for the out/Debug\n" | |
| 318 " build.\n" | |
| 319 "\n" | |
| 320 " gn args out/Debug --list=cpu_arch\n" | |
| 321 " Prints information about the \"cpu_arch\" argument for the out/Debug\n" | |
| 322 " build.\n" | |
| 323 "\n" | |
| 324 " gn args --list --args=\"os=\\\"android\\\" enable_doom_melon=true\"\n" | |
| 325 " Prints all arguments with the default values for a build with the\n" | |
| 326 " given arguments set (which may affect the values of other\n" | |
| 327 " arguments).\n"; | |
| 328 | |
| 329 int RunArgs(const std::vector<std::string>& args) { | |
| 330 if (args.size() != 1) { | |
| 331 Err(Location(), "Exactly one build dir needed.", | |
| 332 "Usage: \"gn args <build_dir>\"\n" | |
| 333 "Or see \"gn help args\" for more variants.").PrintToStdout(); | |
| 334 return 1; | |
| 335 } | |
| 336 | |
| 337 if (base::CommandLine::ForCurrentProcess()->HasSwitch(kSwitchList)) | |
| 338 return ListArgs(args[0]); | |
| 339 return EditArgsFile(args[0]); | |
| 340 } | |
| 341 | |
| 168 } // namespace commands | 342 } // namespace commands |
| OLD | NEW |