Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 #!/usr/bin/python | |
| 2 # Copyright (c) 2010 The Chromium OS Authors. All rights reserved. | |
| 3 # Use of this source code is governed by a BSD-style license that can be | |
| 4 # found in the LICENSE file. | |
| 5 | |
| 6 """Chromite.""" | |
| 7 | |
| 8 # Python imports | |
| 9 import base64 | |
|
sosa
2010/12/23 01:52:27
Maybe sort ascii ... Capitals first, lowercase sec
diandersAtChromium
2011/01/06 00:23:23
No. Current order matches Google python conventio
| |
| 10 import ConfigParser | |
| 11 import cPickle as pickle | |
|
sosa
2010/12/23 01:52:27
it's preferred not to rename.
diandersAtChromium
2011/01/06 00:23:23
Done.
| |
| 12 import optparse | |
| 13 import os | |
| 14 import StringIO | |
| 15 import sys | |
| 16 | |
| 17 | |
| 18 # Ugly chunk of code to find the Chromium OS root and Chromite root, even if | |
| 19 # we're not in the chroot. | |
| 20 if 'CROS_WORKON_SRCROOT' in os.environ: | |
| 21 _SRCROOT_PATH = os.environ['CROS_WORKON_SRCROOT'] | |
| 22 _CHROMITE_PATH = os.path.join(_SRCROOT_PATH, 'src', 'scripts', 'chromite') | |
| 23 else: | |
| 24 _CHROMITE_PATH = os.path.dirname(os.path.realpath(__file__)) | |
| 25 _SRCROOT_PATH = os.path.realpath(os.path.join(_CHROMITE_PATH, | |
|
sosa
2010/12/23 01:52:27
can't you always do this? Just just use the else.
diandersAtChromium
2011/01/06 00:23:23
Done.
| |
| 26 '..', '..', '..')) | |
| 27 sys.path.insert(0, os.path.join(_CHROMITE_PATH, 'lib')) | |
|
sosa
2010/12/23 01:52:27
Can't you do some gooey goodness with __init__.py
diandersAtChromium
2011/01/06 00:23:23
...or, I can just not be completely stupid and cha
| |
| 28 | |
| 29 | |
| 30 # Local library imports | |
| 31 from cros_build_lib import Die | |
| 32 from cros_build_lib import Info | |
| 33 from cros_build_lib import RunCommand | |
| 34 from cros_build_lib import RunCommandError | |
| 35 import text_menu | |
| 36 | |
| 37 | |
| 38 # Usage and description strings for OptionParser. | |
| 39 _USAGE = 'usage: %prog [options] [cmd [build.spec]]' | |
|
sosa
2010/12/23 01:52:27
Define these in your main
diandersAtChromium
2011/01/06 00:23:23
Done.
| |
| 40 _DESCRIPTION = """The chromite script is a wrapper to make it easy to do | |
| 41 various build things. Current commands: %(command_list)s. For a description | |
| 42 of commands, run chromite without any options.""" | |
| 43 | |
| 44 | |
| 45 # A list of paths that we'll search through to find spec files. | |
|
sosa
2010/12/23 01:52:27
Why are these globals? You use both of these once
diandersAtChromium
2011/01/06 00:23:23
Moved to where they are used; they are lists in an
| |
| 46 _BUILD_SPEC_SEARCH_PATH = [ | |
| 47 os.path.join(_CHROMITE_PATH, 'specs', 'build'), | |
| 48 ] | |
| 49 _CHROOT_SPEC_SEARCH_PATH = [ | |
| 50 os.path.join(_CHROMITE_PATH, 'specs', 'chroot'), | |
| 51 ] | |
| 52 | |
| 53 | |
| 54 # Spec files must end with this suffix. | |
| 55 _SPEC_SUFFIX = '.spec' | |
| 56 | |
| 57 | |
| 58 # This "file" will be fed into the SafeConfigParser to provide defaults. | |
| 59 _DEFAULT_BUILD_SPEC = """ | |
|
sosa
2010/12/23 01:52:27
Why are these defined here vs legacy.spc etc
diandersAtChromium
2011/01/06 00:23:23
Done. Moved to specs/build/_defaults and specs/ch
| |
| 60 [LEGACY] | |
| 61 board: %(name)s | |
| 62 chroot_spec: chroot | |
| 63 | |
| 64 [LEGACY_BUILD] | |
| 65 setup_board_flags: | |
| 66 build_packages_flags: | |
| 67 | |
| 68 [LEGACY_IMAGE] | |
| 69 build_image_flags: | |
| 70 """ | |
| 71 | |
| 72 # This "file" will be fed into the SafeConfigParser to provide defaults. | |
| 73 _DEFAULT_CHROOT_SPEC = """ | |
| 74 [LEGACY_CHROOT] | |
| 75 path: %(name)s | |
| 76 make_chroot_flags: | |
| 77 enter_chroot_flags: | |
| 78 """ | |
| 79 | |
| 80 # Define command handlers and command strings. We define them in this way | |
| 81 # so that someone searching for what calls _CmdXyz can find it easy with a grep. | |
| 82 # | |
| 83 # ORDER MATTERS here when we show the menu. | |
| 84 _COMMAND_HANDLERS = [ | |
| 85 '_CmdBuild', | |
| 86 '_CmdClean', | |
| 87 '_CmdDistClean', | |
| 88 ] | |
| 89 _COMMAND_STRS = [fn_str[len('_Cmd'):].lower() for fn_str in _COMMAND_HANDLERS] | |
| 90 | |
| 91 | |
| 92 def _IsInsideChroot(): | |
|
sosa
2010/12/23 01:52:27
Already defined in chromite lib
diandersAtChromium
2011/01/06 00:23:23
Done.
| |
| 93 """Returns True if we're inside the chroot; False if not.""" | |
| 94 return os.path.exists('/etc/debian_chroot') | |
| 95 | |
| 96 | |
| 97 def _GetBoardDir(build_config): | |
| 98 """Returns the board directory (inside the chroot) given the name. | |
| 99 | |
| 100 Args: | |
| 101 build_config: A SafeConfigParser representing the config that we're | |
| 102 building. | |
| 103 | |
| 104 Returns: | |
| 105 The absolute path to the board. | |
| 106 """ | |
| 107 board_name = build_config.get('LEGACY', 'board') | |
| 108 | |
| 109 # Extra checks on these, since we sometimes might do a rm -f on the board | |
| 110 # directory and these could cause some really bad behavior. | |
| 111 assert board_name, "Didn't expect blank board name." | |
| 112 assert len(board_name.split()) == 1, 'Board name should have no white space.' | |
| 113 | |
| 114 return os.path.join('/', 'build', board_name) | |
| 115 | |
| 116 | |
| 117 def _GetChrootAbsDir(chroot_config): | |
| 118 """Returns the absolute chroot directory the chroot config. | |
| 119 | |
| 120 Args: | |
| 121 chroot_config: A SafeConfigParser representing the config for the chroot. | |
| 122 Returns: | |
| 123 The chroot directory, always absolute. | |
| 124 """ | |
| 125 chroot_dir = chroot_config.get('LEGACY_CHROOT', 'path') | |
| 126 return os.path.join(_SRCROOT_PATH, chroot_dir) | |
| 127 | |
| 128 | |
| 129 def _FindCommand(cmd_name): | |
|
sosa
2010/12/23 01:52:27
Def good candidate for unit test. Mock out TextMe
| |
| 130 """Find the command that matches the given command name. | |
| 131 | |
| 132 This tries to be smart. See the cmd_name parameter for details. | |
| 133 | |
| 134 Args: | |
| 135 cmd_name: Can be any of the following: | |
| 136 1. The full name of a command. This is checked first so that if one | |
| 137 command name is a substring of another, you can still specify | |
| 138 the shorter spec name and know you won't get a menu (the exact | |
| 139 match prevents the menu). | |
| 140 2. A substring that will be used to pare-down a menu of commands | |
| 141 Can be the empty string to show a menu of all commands | |
| 142 | |
| 143 Returns: | |
| 144 The command name. | |
| 145 """ | |
| 146 # Always make cmd_name lower. Commands are case-insensitive. | |
| 147 cmd_name = cmd_name.lower() | |
| 148 | |
| 149 # If we're an exact match, we're done! | |
| 150 if cmd_name in _COMMAND_STRS: | |
| 151 return cmd_name | |
| 152 | |
| 153 # Find ones that match and put them in a menu... | |
| 154 possible_cmds = [] | |
| 155 possible_choices = [] | |
| 156 for cmd_num, this_cmd in enumerate(_COMMAND_STRS): | |
| 157 if cmd_name in this_cmd: | |
|
sosa
2010/12/23 01:52:27
regex is better. "in" does any substring ... not
diandersAtChromium
2011/01/06 00:23:23
Changed to startswith(). You're right that prefix
| |
| 158 handler = eval(_COMMAND_HANDLERS[cmd_num]) | |
| 159 assert hasattr(handler, '__doc__'), \ | |
| 160 ('All handlers must have docstrings: %s' % cmd_name) | |
| 161 desc = handler.__doc__.splitlines()[0] | |
| 162 | |
| 163 possible_cmds.append(this_cmd) | |
| 164 possible_choices.append('%s - %s' % (this_cmd, desc)) | |
| 165 | |
| 166 if not possible_choices: | |
| 167 Die('No commands matched: "%s". ' | |
|
sosa
2010/12/23 01:52:27
I would consider throwing exceptions rather than d
diandersAtChromium
2011/01/06 00:23:23
I don't think so in this case. Exceptions are bet
sosa
2011/01/11 04:44:22
I can go either way.
On 2011/01/06 00:23:23, di
| |
| 168 'Try running with no arguments for a menu.' % | |
| 169 cmd_name) | |
| 170 | |
| 171 if len(possible_choices) == 1: | |
| 172 choice = 0 | |
| 173 Info("Running command '%s'.\n" % possible_cmds[choice]) | |
| 174 else: | |
| 175 choice = text_menu.TextMenu(possible_choices, 'Which chromite command:', | |
|
sosa
2010/12/23 01:52:27
neat!
| |
| 176 menu_width=0) | |
| 177 return possible_cmds[choice] | |
| 178 | |
| 179 | |
| 180 def _FindSpec(spec_name, is_chroot_spec=False, can_show_ui=True): | |
| 181 """Find the spec with the given name. | |
| 182 | |
| 183 This tries to be smart about helping the user to find the right spec. See | |
| 184 the spec_name parameter for details. | |
| 185 | |
| 186 Args: | |
| 187 spec_name: Can be any of the following: | |
| 188 1. A full path to a spec file (including the .spec suffix). This is | |
| 189 checked first (i.e. if os.path.isfile(spec_name), we assume we've | |
| 190 got this case). | |
| 191 2. The full name of a spec file somewhere in the spec search path | |
|
sosa
2010/12/23 01:52:27
I think it'd be better to combine 2 and 3 into one
diandersAtChromium
2011/01/06 00:23:23
No. I really wanted a substring for finding the s
| |
| 192 (not including the .spec suffix). This is checked second. Putting | |
| 193 this check second means that if one spec name is a substring of | |
| 194 another, you can still specify the shorter spec name and know you | |
| 195 won't get a menu (the exact match prevents the menu). | |
| 196 3. A substring that will be used to pare-down a menu of spec files | |
| 197 found in the spec search path. Can be the empty string to show a | |
| 198 menu of all spec files in the spec path. NOTE: Only possible if | |
| 199 can_show_ui is True. | |
| 200 is_chroot_spec: If True, this is a chroot spec. | |
| 201 can_show_ui: If True, enables the spec name to be a substring since we can | |
| 202 then show a menu if the substring matches more than one thing. | |
| 203 | |
| 204 Returns: | |
| 205 A path to the spec file. | |
| 206 """ | |
| 207 # Get a string for the spec type (for debugging) | |
| 208 if is_chroot_spec: | |
| 209 spec_type = 'chroot' | |
| 210 else: | |
| 211 spec_type = 'build' | |
| 212 | |
| 213 # If we have an exact path name, that's it. No searching. | |
| 214 if os.path.isfile(spec_name): | |
| 215 return spec_name | |
| 216 | |
| 217 # Figure out what our search path should be. | |
| 218 if is_chroot_spec: | |
| 219 search_path = _CHROOT_SPEC_SEARCH_PATH | |
| 220 else: | |
| 221 search_path = _BUILD_SPEC_SEARCH_PATH | |
| 222 | |
| 223 # Look for an exact match of a spec name. An exact match will go through with | |
| 224 # no menu. | |
| 225 for dir_path in search_path: | |
| 226 spec_path = os.path.join(dir_path, spec_name + _SPEC_SUFFIX) | |
| 227 if os.path.isfile(spec_path): | |
| 228 return spec_path | |
| 229 | |
| 230 # Die right away if we can't show UI and didn't have an exact match. | |
| 231 if not can_show_ui: | |
| 232 Die("Couldn't find %s spec: %s" % (spec_type, spec_name)) | |
| 233 | |
| 234 # No full path and no exact match. Move onto a menu. | |
| 235 # First step is to construct the options. We'll store in a dict keyed by | |
| 236 # spec name. | |
| 237 options = {} | |
| 238 for dir_path in search_path: | |
| 239 for file_name in os.listdir(dir_path): | |
| 240 file_path = os.path.join(dir_path, file_name) | |
| 241 file_base_path, _ = os.path.splitext(file_path) | |
| 242 | |
| 243 # Skip if this isn't a spec file. Use samefile to check, which I think | |
| 244 # avoids any case-sensitiveness in the filesystem. | |
| 245 if not os.path.samefile(file_path, file_base_path + _SPEC_SUFFIX): | |
| 246 continue | |
|
sosa
2010/12/23 01:52:27
add line after
diandersAtChromium
2011/01/06 00:23:23
Done.
| |
| 247 this_spec_name, _ = os.path.splitext(file_name) | |
| 248 | |
| 249 # Skip if this spec file doesn't contain our substring. We are _always_ | |
| 250 # case insensitive here to be helpful to the user. | |
| 251 if spec_name.lower() not in this_spec_name.lower(): | |
| 252 continue | |
| 253 | |
| 254 # Skip if we've already seen this spec. This means that if the same spec | |
| 255 # name is in multiple parts of the path, we'll only match the first one. | |
| 256 if this_spec_name in options: | |
| 257 continue | |
| 258 | |
| 259 # OK, it's good. Store the path. | |
| 260 options[this_spec_name] = file_path | |
|
sosa
2010/12/23 01:52:27
You could probably merge the majority of this func
diandersAtChromium
2011/01/06 00:23:23
I'd rather not merge them. It is true that there
| |
| 261 | |
| 262 # If no match, die. | |
| 263 if not options: | |
| 264 Die("Couldn't find any matching %s specs for: %s" % (spec_type, spec_name)) | |
| 265 | |
| 266 # If exactly one match, we're done. | |
| 267 if len(options) == 1: | |
| 268 _, spec_path = options.popitem() | |
| 269 return spec_path | |
| 270 | |
| 271 # If more than one, show a menu... | |
| 272 option_keys = sorted(options.iterkeys()) | |
| 273 choice = text_menu.TextMenu(option_keys, 'Choose a build spec:') | |
| 274 return options[option_keys[choice]] | |
| 275 | |
| 276 | |
| 277 def _ParseCommandLine(arguments): | |
| 278 """Parse the command line to figure out the build command and build spec. | |
| 279 | |
| 280 Note that the command and buildspec are specified directly on the command | |
| 281 line (not using "options") because every command needs to specify them and | |
| 282 we want to make it that much easier for people to type the commands. Also | |
| 283 note that the buildspec can be specified as any substring of the spec name | |
| 284 (and if that specifies more than one thing, we'll show a menu). | |
| 285 | |
| 286 That means you can run chromite like this: | |
| 287 chromite build mario | |
| 288 ...instead of: | |
| 289 chromite --cmd=build --board=x86-mario | |
| 290 | |
| 291 If the user specified a bad command line, this function will call Die(). | |
| 292 | |
| 293 Args: | |
| 294 arguments: The non options from the OptionParser. | |
| 295 | |
| 296 Returns: | |
| 297 chromite_cmd: The chromite command that was specified (default is "build"). | |
| 298 build_spec_path: The path to the build spec. | |
| 299 """ | |
| 300 # Set defaults. | |
| 301 chromite_cmd = '' | |
| 302 spec_name = '' | |
| 303 | |
| 304 # Make a copy of arguments so we can futz with it and caller doesn't see it | |
| 305 # change... | |
| 306 arguments = list(arguments) | |
|
sosa
2010/12/23 01:52:27
[] is a shorthand for list() is it not?
diandersAtChromium
2011/01/06 00:23:23
You are saying to do this instead?
arguments = a
sosa
2011/01/11 04:44:22
I meant arguments = [arguments]. Are these not eq
diandersAtChromium
2011/01/12 00:04:45
Not equivalent.
In [1]: l = range(10)
In [2]: li
| |
| 307 | |
| 308 # Pull the command off if it was specified. | |
| 309 if arguments: | |
| 310 chromite_cmd = arguments.pop(0).lower() | |
| 311 | |
| 312 # Validate the chromite_cmd, popping a menu if needed. | |
| 313 try: | |
| 314 chromite_cmd = _FindCommand(chromite_cmd) | |
| 315 except KeyboardInterrupt: | |
| 316 Die('OK, cancelling...') | |
|
sosa
2010/12/23 01:52:27
Maybe you should add a extra option that does exit
diandersAtChromium
2011/01/06 00:23:23
Done.
| |
| 317 | |
| 318 # There should be 0 or 1 more arguments. | |
| 319 # If too many: error | |
| 320 # If 1, it's the spec name. | |
| 321 # If 0, we'll use the default spec name. | |
| 322 if len(arguments) > 1: | |
| 323 Die('Too many arguments. Try passing --help.') | |
| 324 elif arguments: | |
| 325 spec_name = arguments[0] | |
| 326 | |
| 327 # Find the spec given the name... | |
| 328 try: | |
| 329 build_spec_path = _FindSpec(spec_name) | |
| 330 except KeyboardInterrupt: | |
| 331 Die('OK, cancelling...') | |
|
sosa
2010/12/23 01:52:27
same
diandersAtChromium
2011/01/06 00:23:23
Done.
| |
| 332 | |
| 333 return chromite_cmd, build_spec_path | |
| 334 | |
| 335 | |
| 336 def _ReadConfigs(build_spec_path): | |
| 337 """Read the build_config and chroot_config from spec files. | |
| 338 | |
| 339 Args: | |
| 340 build_spec_path: The path to the build spec. | |
| 341 | |
| 342 Returns: | |
| 343 build_config: A SafeConfigParser representing the config that we're | |
| 344 building. | |
| 345 build_config: A SafeConfigParser representing the config of the chroot. | |
| 346 """ | |
| 347 build_spec_name, _ = os.path.splitext(os.path.basename(build_spec_path)) | |
| 348 | |
| 349 build_config = ConfigParser.SafeConfigParser() | |
| 350 default_build_spec = _DEFAULT_BUILD_SPEC % dict(name=build_spec_name) | |
|
sosa
2010/12/23 01:52:27
{} not dict
diandersAtChromium
2011/01/06 00:23:23
Done.
| |
| 351 build_config.readfp(StringIO.StringIO(default_build_spec), '<defaults>') | |
| 352 build_config.read(build_spec_path) | |
| 353 | |
| 354 chroot_spec = build_config.get('LEGACY', 'chroot_spec') | |
|
sosa
2010/12/23 01:52:27
erm why read specified build spec but legacy chroo
diandersAtChromium
2011/01/06 00:23:23
This is the chroot spec specified in the LEGACY se
sosa
2011/01/11 04:44:22
ping @anush
On 2011/01/06 00:23:23, diandersAtChr
| |
| 355 chroot_spec_path = _FindSpec(chroot_spec, is_chroot_spec=True, | |
| 356 can_show_ui=False) | |
| 357 chroot_spec_name, _ = os.path.splitext(os.path.basename(chroot_spec_path)) | |
| 358 | |
| 359 default_chroot_spec = _DEFAULT_CHROOT_SPEC % dict(name=chroot_spec_name) | |
|
sosa
2010/12/23 01:52:27
{}
diandersAtChromium
2011/01/06 00:23:23
Done.
| |
| 360 chroot_config = ConfigParser.SafeConfigParser() | |
| 361 chroot_config.readfp(StringIO.StringIO(default_chroot_spec), '<defaults>') | |
| 362 chroot_config.read(chroot_spec_path) | |
| 363 | |
| 364 return build_config, chroot_config | |
| 365 | |
| 366 | |
| 367 def _DoesChrootExist(chroot_config): | |
| 368 """Returns whether the chroot already appears to exist. | |
| 369 | |
| 370 Args: | |
| 371 chroot_config: A SafeConfigParser representing the config for the chroot. | |
| 372 | |
| 373 Returns: | |
| 374 True if the chroot appears to exist; False if it appears not to exist. | |
| 375 Note that we're just checking for the existence of the folder--we don't | |
| 376 check whether the chroot was properly configured. | |
| 377 """ | |
| 378 chroot_dir = _GetChrootAbsDir(chroot_config) | |
| 379 return os.path.isdir(chroot_dir) | |
| 380 | |
| 381 | |
| 382 def _DoMakeChroot(chroot_config): | |
| 383 """Build the chroot, if needed. | |
| 384 | |
| 385 Args: | |
| 386 chroot_config: A SafeConfigParser representing the config for the chroot. | |
| 387 """ | |
| 388 # Skip this whole command if things already exist. | |
| 389 # TODO(dianders): Theoretically, calling make_chroot a second time is OK | |
| 390 # and "fixes up" the chroot. ...but build_packages will do the fixups | |
| 391 # anyway (I think), so this isn't that important. | |
| 392 chroot_dir = _GetChrootAbsDir(chroot_config) | |
|
sosa
2010/12/23 01:52:27
use DoesChrootExist()
diandersAtChromium
2011/01/06 00:23:23
Added ability for _GetChrootAbsDir() to check and
| |
| 393 if os.path.isdir(chroot_dir): | |
| 394 Info('%s already exists, skipping make_chroot.' % chroot_dir) | |
| 395 return | |
| 396 | |
| 397 Info('MAKING THE CHROOT') | |
| 398 | |
| 399 # Put together command. We're going to force the shell to do all of the | |
| 400 # splitting of arguments, since we're throwing all of the flags from the | |
| 401 # config file in there. | |
| 402 cmd = './make_chroot --chroot="%s" %s' % ( | |
|
sosa
2010/12/23 01:52:27
Why are you setting --chroot here if we always use
diandersAtChromium
2011/01/06 00:23:23
Boards can specify alternate chroots. The --chroo
| |
| 403 chroot_dir, | |
| 404 chroot_config.get('LEGACY_CHROOT', 'make_chroot_flags') | |
| 405 ) | |
| 406 | |
| 407 # We'll put CWD as src/scripts when running the command. Since everyone | |
| 408 # running by hand has their cwd there, it is probably the safest. | |
| 409 cwd = os.path.join(_SRCROOT_PATH, 'src', 'scripts') | |
| 410 | |
| 411 # Run it. Pass any failures upward. | |
| 412 try: | |
| 413 RunCommand(cmd, shell=True, cwd=cwd, ignore_sigint=True) | |
| 414 except RunCommandError, e: | |
| 415 Die(str(e)) | |
|
sosa
2010/12/23 01:52:27
don't catch it
diandersAtChromium
2011/01/06 00:23:23
Done.
| |
| 416 | |
| 417 | |
| 418 def _DoEnterChroot(chroot_config, fn, *args, **kwargs): | |
| 419 """Re-run the given function inside the chroot. | |
| 420 | |
| 421 When the function is run, it will be run in a SEPARATE INSTANCE of chromite, | |
| 422 which will be run in the chroot. This is a little weird. Specifically: | |
| 423 - When the callee executes, it will be a separate python instance. | |
| 424 - Globals will be reset back to defaults. | |
| 425 - A different version of python (with different modules) may end up running | |
| 426 the script in the chroot. | |
| 427 - All arguments are pickled up, encoded, and sent through the command line. | |
| 428 - That means that args must be pickleable and not too huge. | |
| 429 - It also means that modifications to the parameters by the callee are not | |
| 430 visible to the caller. | |
| 431 - Even the function is "pickled". The way the pickle works, I belive it | |
| 432 just passes the name of the function. If this name somehow resolves | |
| 433 differently in the chroot, you may get weirdness. | |
| 434 - Since we're in the chroot, obviously files may have different paths. It's | |
| 435 up to you to convert parameters if you need to. | |
| 436 - The stdin, stdout, and stderr aren't touched. | |
| 437 | |
| 438 Args: | |
| 439 chroot_config: A SafeConfigParser representing the config for the chroot. | |
| 440 fn: The function to call. | |
| 441 args: All other arguments will be passed to the function as is. | |
| 442 kwargs: All other arguments will be passed to the function as is. | |
| 443 """ | |
| 444 Info('ENTERING THE CHROOT') | |
| 445 | |
| 446 # Encode our state in something that can be passed on the command line. | |
| 447 # TODO(dianders): What kind of limits do we have here? 64K? | |
| 448 resume_state = pickle.dumps((fn, args, kwargs), pickle.HIGHEST_PROTOCOL) | |
| 449 resume_state = base64.b64encode(resume_state) | |
|
sosa
2010/12/23 01:52:27
Can you store state to a temp file? tempfile.mkst
diandersAtChromium
2011/01/06 00:23:23
I researched this and it looks like I have 2 megs
| |
| 450 | |
| 451 # Put together command. We're going to force the shell to do all of the | |
| 452 # splitting of arguments, since we're throwing all of the flags from the | |
| 453 # config file in there. | |
| 454 # TODO(dianders): Once chromite is in the path inside the chroot, we should | |
| 455 # change it from 'chromite/chromite' to just 'chromite'. | |
| 456 cmd = ( | |
| 457 './enter_chroot.sh --chroot="%s" %s --' | |
| 458 ' chromite/chromite --resume-state=%s') % ( | |
| 459 _GetChrootAbsDir(chroot_config), | |
| 460 chroot_config.get('LEGACY_CHROOT', 'enter_chroot_flags'), | |
| 461 resume_state) | |
| 462 | |
| 463 # We'll put CWD as src/scripts when running the command. Since everyone | |
| 464 # running by hand has their cwd there, it is probably the safest. | |
| 465 cwd = os.path.join(_SRCROOT_PATH, 'src', 'scripts') | |
| 466 | |
| 467 # Run it. We allow "error" so we don't print a confusing error message | |
| 468 # filled with out resume-state garbage on control-C. | |
| 469 cmd_result = RunCommand(cmd, shell=True, cwd=cwd, print_cmd=False, | |
| 470 exit_code=True, error_ok=True, ignore_sigint=True) | |
| 471 | |
| 472 if cmd_result.returncode: | |
| 473 Die('Chroot exited with error code %d' % cmd_result.returncode) | |
| 474 | |
| 475 | |
| 476 def _DoSetupBoard(build_config): | |
| 477 """Setup the board, if needed. | |
| 478 | |
| 479 This just runs the setup_board command with the proper args, if needed. | |
| 480 | |
| 481 Args: | |
| 482 build_config: A SafeConfigParser representing the build config. | |
| 483 """ | |
| 484 # Skip this whole command if things already exist. | |
| 485 board_dir = _GetBoardDir(build_config) | |
|
sosa
2010/12/23 01:52:27
How do you --force?
diandersAtChromium
2011/01/06 00:23:23
You don't. You do a ./chromite clean first. This
sosa
2011/01/11 04:44:22
Difference between
make_chroot --replace
and setu
diandersAtChromium
2011/01/12 00:04:45
Done.
| |
| 486 if os.path.isdir(board_dir): | |
| 487 Info('%s already exists, skipping setup_board.' % board_dir) | |
| 488 return | |
| 489 | |
| 490 Info('SETTING UP THE BOARD') | |
| 491 | |
| 492 # Put together command. We're going to force the shell to do all of the | |
| 493 # splitting of arguments, since we're throwing all of the flags from the | |
| 494 # config file in there. | |
| 495 cmd = './setup_board --board="%s" %s' % ( | |
| 496 build_config.get('LEGACY', 'board'), | |
| 497 build_config.get('LEGACY_BUILD', 'setup_board_flags') | |
| 498 ) | |
| 499 | |
| 500 # We'll put CWD as src/scripts when running the command. Since everyone | |
| 501 # running by hand has their cwd there, it is probably the safest. | |
| 502 cwd = os.path.join(_SRCROOT_PATH, 'src', 'scripts') | |
| 503 | |
| 504 # Run it. Exit upon failure. | |
| 505 try: | |
| 506 RunCommand(cmd, shell=True, cwd=cwd, ignore_sigint=True) | |
| 507 except RunCommandError, e: | |
| 508 Die(str(e)) | |
|
sosa
2010/12/23 01:52:27
ditto on the die here
diandersAtChromium
2011/01/06 00:23:23
Done.
| |
| 509 | |
| 510 | |
| 511 def _DoBuildPackages(build_config): | |
| 512 """Build packages. | |
| 513 | |
| 514 This just runs the build_packages command with the proper args. | |
| 515 | |
| 516 Args: | |
| 517 build_config: A SafeConfigParser representing the build config. | |
| 518 """ | |
| 519 Info('BUILDING PACKAGES') | |
| 520 | |
| 521 # Put together command. We're going to force the shell to do all of the | |
| 522 # splitting of arguments, since we're throwing all of the flags from the | |
| 523 # config file in there. | |
| 524 cmd = './build_packages --board="%s" %s' % ( | |
| 525 build_config.get('LEGACY', 'board'), | |
| 526 build_config.get('LEGACY_BUILD', 'build_packages_flags') | |
| 527 ) | |
| 528 | |
| 529 # We'll put CWD as src/scripts when running the command. Since everyone | |
| 530 # running by hand has their cwd there, it is probably the safest. | |
| 531 cwd = os.path.join(_SRCROOT_PATH, 'src', 'scripts') | |
| 532 | |
| 533 # Run it. Exit upon failure. | |
| 534 try: | |
| 535 RunCommand(cmd, shell=True, cwd=cwd, ignore_sigint=True) | |
| 536 except RunCommandError, e: | |
| 537 Die(str(e)) | |
| 538 | |
| 539 | |
| 540 def _DoBuildImage(build_config): | |
| 541 """Build an image. | |
| 542 | |
| 543 This just runs the build_image command with the proper args. | |
| 544 | |
| 545 Args: | |
| 546 build_config: A SafeConfigParser representing the build config. | |
| 547 """ | |
| 548 Info('BUILDING THE IMAGE') | |
| 549 | |
| 550 # Put together command. We're going to force the shell to do all of the | |
| 551 # splitting of arguments, since we're throwing all of the flags from the | |
| 552 # config file in there. | |
| 553 cmd = './build_image --board="%s" %s' % ( | |
| 554 build_config.get('LEGACY', 'board'), | |
| 555 build_config.get('LEGACY_IMAGE', 'build_image_flags') | |
| 556 ) | |
| 557 | |
| 558 # We'll put CWD as src/scripts when running the command. Since everyone | |
| 559 # running by hand has their cwd there, it is probably the safest. | |
| 560 cwd = os.path.join(_SRCROOT_PATH, 'src', 'scripts') | |
| 561 | |
| 562 # Run it. Exit upon failure. | |
| 563 try: | |
| 564 RunCommand(cmd, shell=True, cwd=cwd, ignore_sigint=True) | |
| 565 except RunCommandError, e: | |
| 566 Die(str(e)) | |
| 567 | |
| 568 | |
| 569 def _CmdBuild(chromite_cmd, build_config, chroot_config, options): | |
| 570 """Build the chroot, the packages, and the image for a board. | |
| 571 | |
| 572 This is the main chromite command and builds an image for you. | |
| 573 | |
| 574 Args: | |
| 575 chromite_cmd: The command that was called. | |
| 576 build_config: A SafeConfigParser representing the build config. | |
| 577 chroot_config: A SafeConfigParser representing the chroot config. | |
| 578 options: Options from the OptionParser | |
| 579 """ | |
| 580 if not _IsInsideChroot(): | |
| 581 _DoMakeChroot(chroot_config) | |
| 582 _DoEnterChroot(chroot_config, _CmdBuild, chromite_cmd, build_config, | |
| 583 chroot_config, options) | |
| 584 else: | |
| 585 _DoSetupBoard(build_config) | |
| 586 _DoBuildPackages(build_config) | |
| 587 _DoBuildImage(build_config) | |
| 588 | |
| 589 | |
| 590 def _CmdClean(chromite_cmd, build_config, chroot_config, options): | |
| 591 """Clean out built packages for a board. | |
|
sosa
2010/12/23 01:52:27
Wait, what is this for? When would we want to do t
diandersAtChromium
2011/01/06 00:23:23
You would do this instead of setup_board --force.
sosa
2011/01/11 04:44:22
That is nice!
On 2011/01/06 00:23:23, diandersAtC
diandersAtChromium
2011/01/12 00:04:45
Done.
| |
| 592 | |
| 593 Args: | |
| 594 chromite_cmd: The command that was called. | |
| 595 build_config: A SafeConfigParser representing the build config. | |
| 596 chroot_config: A SafeConfigParser representing the chroot config. | |
| 597 options: Options from the OptionParser | |
| 598 """ | |
| 599 # These vars are part of the standard signature, but not used. | |
| 600 _ = chromite_cmd | |
| 601 | |
| 602 # We'll need the directory so we can delete stuff. | |
| 603 board_dir = _GetBoardDir(build_config) | |
| 604 | |
| 605 if not _IsInsideChroot(): | |
| 606 if not _DoesChrootExist(chroot_config): | |
| 607 Die("Chroot doesn't appear to exist, nothing to do.") | |
| 608 | |
| 609 # We'll need to make the board directory relative to the chroot. | |
| 610 chroot_dir = _GetChrootAbsDir(chroot_config) | |
| 611 assert board_dir.startswith('/'), 'Expected unix-style, absolute path.' | |
| 612 board_dir = board_dir.lstrip('/') | |
| 613 board_dir = os.path.join(chroot_dir, board_dir) | |
| 614 | |
| 615 if not os.path.isdir(board_dir): | |
| 616 Die("Nothing to clean: the board directory doesn't exist.\n %s" % | |
| 617 board_dir) | |
| 618 | |
| 619 if not options.yes: | |
| 620 sys.stderr.write('\n' | |
| 621 'Board dir is at: %s\n' | |
| 622 'Are you sure you want to delete it (YES/NO)? ' % | |
| 623 board_dir) | |
| 624 answer = raw_input() | |
| 625 if answer != 'YES': | |
| 626 Die("You must answer exactly 'YES' if you want to proceed.") | |
| 627 | |
| 628 # Since we're about to do a sudo rm -rf, these are just extra precautions. | |
| 629 # This shouldn't be the only place testing these (assert fails are ugly and | |
| 630 # can be turned off), but better safe than sorry. | |
| 631 # Note that the restriction on '*' is a bit unnecessary, since no shell | |
| 632 # expansion should happen. ...but again, I'd rather be safe. | |
| 633 assert os.path.isabs(board_dir), 'Board dir better be absolute' | |
| 634 assert board_dir != '/', 'Board dir better not be /' | |
| 635 assert '*' not in board_dir, 'Board dir better not have any *s' | |
| 636 assert build_config.get('LEGACY', 'board'), 'Board better not be blank' | |
| 637 assert build_config.get('LEGACY', 'board') in board_dir, \ | |
| 638 'Board name better be in board dir' | |
| 639 | |
| 640 args = ['sudo', '--', 'rm', '-rf', board_dir] | |
| 641 RunCommand(args) | |
| 642 Info('Deleted: %s' % board_dir) | |
| 643 | |
| 644 | |
| 645 def _CmdDistClean(chromite_cmd, build_config, chroot_config, options): | |
| 646 """Delete the chroot. | |
| 647 | |
| 648 Args: | |
| 649 chromite_cmd: The command that was called. | |
| 650 build_config: A SafeConfigParser representing the build config. | |
| 651 chroot_config: A SafeConfigParser representing the chroot config. | |
| 652 options: Options from the OptionParser | |
| 653 """ | |
| 654 # These vars are part of the standard signature, but not used. | |
| 655 _ = (chromite_cmd, build_config) | |
| 656 | |
| 657 if _IsInsideChroot(): | |
| 658 Die('Please exit the chroot before running distclean.') | |
| 659 | |
| 660 chroot_dir = _GetChrootAbsDir(chroot_config) | |
| 661 | |
| 662 if not os.path.isdir(chroot_dir): | |
|
sosa
2010/12/23 01:52:27
This isn't necessary. Make_chroot --delete handle
diandersAtChromium
2011/01/06 00:23:23
Yes, but it is a nicety to do this check before gi
sosa
2011/01/11 04:44:22
The question is fine. I specifically meant the os
diandersAtChromium
2011/01/12 00:04:45
Done.
| |
| 663 Die("Nothing to clean: the chroot doesn't exist.\n %s" % | |
| 664 chroot_dir) | |
| 665 | |
| 666 if not options.yes: | |
| 667 sys.stderr.write('\n' | |
| 668 'Chroot is at: %s\n' | |
| 669 'Are you sure you want to delete it (YES/NO)? ' % | |
| 670 chroot_dir) | |
| 671 answer = raw_input() | |
| 672 if answer != 'YES': | |
| 673 Die("You must answer exactly 'YES' if you want to proceed.") | |
| 674 | |
| 675 # Can pass args and not shell=True, since no user flags. :) | |
| 676 args = ['./make_chroot', '--chroot=%s' % chroot_dir, '--delete'] | |
| 677 | |
| 678 # We'll put CWD as src/scripts when running the command. Since everyone | |
| 679 # running by hand has their cwd there, it is probably the safest. | |
| 680 cwd = os.path.join(_SRCROOT_PATH, 'src', 'scripts') | |
| 681 | |
| 682 # Run it. Pass any failures upward. | |
| 683 RunCommand(args, cwd=cwd) | |
| 684 | |
| 685 | |
| 686 def main(): | |
| 687 command_list = ', '.join(sorted(_COMMAND_STRS)) | |
| 688 parser = optparse.OptionParser( | |
| 689 usage=_USAGE, description=_DESCRIPTION % dict(command_list=command_list) | |
| 690 ) | |
| 691 parser.add_option('-y', '--yes', default=False, action='store_true', | |
| 692 help='Answer "YES" to "are you sure?" questions.') | |
| 693 parser.add_option('--resume-state', default='', | |
| 694 help='Base64 of state pickle (internal use only).') | |
| 695 (options, arguments) = parser.parse_args() | |
| 696 | |
| 697 if options.resume_state: | |
| 698 # We've called to resume ourselves in the chroot. | |
| 699 fn, args, kwargs = pickle.loads(base64.b64decode(options.resume_state)) | |
| 700 fn(*args, **kwargs) | |
| 701 else: | |
| 702 chromite_cmd, build_spec_path = _ParseCommandLine(arguments) | |
| 703 Info("Running command '%s' with spec:\n %s" % (chromite_cmd, | |
| 704 build_spec_path)) | |
| 705 | |
| 706 build_config, chroot_config = _ReadConfigs(build_spec_path) | |
| 707 | |
| 708 cmd_fn = eval(_COMMAND_HANDLERS[_COMMAND_STRS.index(chromite_cmd)]) | |
| 709 cmd_fn(chromite_cmd, build_config, chroot_config, options) | |
| 710 | |
|
sosa
2010/12/23 01:52:27
2 lines here
diandersAtChromium
2011/01/06 00:23:23
Done.
| |
| 711 if __name__ == '__main__': | |
| 712 main() | |
| OLD | NEW |