Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 #!/usr/bin/python | 1 #!/usr/bin/python |
| 2 | 2 |
| 3 import copy | 3 import copy |
| 4 import gyp.common | 4 import gyp.common |
| 5 import optparse | 5 import optparse |
| 6 import os.path | 6 import os.path |
| 7 import re | 7 import re |
| 8 import shlex | 8 import shlex |
| 9 import subprocess | 9 import subprocess |
| 10 import sys | 10 import sys |
| (...skipping 326 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 337 match = match_group.groupdict() | 337 match = match_group.groupdict() |
| 338 # match['replace'] is the substring to look for, match['type'] | 338 # match['replace'] is the substring to look for, match['type'] |
| 339 # is the character code for the replacement type (< > <! >! <@ | 339 # is the character code for the replacement type (< > <! >! <@ |
| 340 # >@ <!@ >!@), match['is_array'] contains a '[' for command | 340 # >@ <!@ >!@), match['is_array'] contains a '[' for command |
| 341 # arrays, and match['content'] is the name of the variable (< >) | 341 # arrays, and match['content'] is the name of the variable (< >) |
| 342 # or command to run (<! >!). | 342 # or command to run (<! >!). |
| 343 | 343 |
| 344 # run_command is true if a ! variant is used. | 344 # run_command is true if a ! variant is used. |
| 345 run_command = '!' in match['type'] | 345 run_command = '!' in match['type'] |
| 346 | 346 |
| 347 # Capture these now so we can adjust them later. | |
| 348 replace_start = match_group.start('replace') | |
| 349 replace_end = match_group.end('replace') | |
| 350 | |
| 351 # Find the ending paren, and re-evaluate the contained string. | |
| 352 (c_start, c_end) = FindEnclosingBracketGroup(input[replace_start:]) | |
| 353 | |
| 354 # Adjust the replacement range to match the entire command | |
| 355 # found by FindEnclosingBracketGroup (since the variable_re | |
| 356 # probably doesn't match the entire command if it contained | |
| 357 # nested variables). | |
| 358 replace_end = replace_start + c_end | |
| 359 | |
| 360 # Find the "real" replacement, matching the appropriate closing | |
| 361 # paren, and adjust the replacement start and end. | |
| 362 replacement = input[replace_start:replace_end] | |
| 363 | |
| 364 # Figure out what the contents of the variable parens are. | |
| 365 contents_start = replace_start + c_start + 1 | |
| 366 contents_end = replace_end - 1 | |
| 367 contents = input[contents_start:contents_end] | |
| 368 | |
| 369 # Recurse to expand variables in the contents | |
| 370 contents = ExpandVariables(contents, is_late, variables, build_file) | |
| 371 | |
| 372 # Strip off leading/trailing whitespace so that variable matches are | |
| 373 # simpler below (and because they are rarely needed). | |
| 374 contents = contents.strip() | |
| 375 | |
| 347 # expand_to_list is true if an @ variant is used. In that case, | 376 # expand_to_list is true if an @ variant is used. In that case, |
| 348 # the expansion should result in a list. Note that the caller | 377 # the expansion should result in a list. Note that the caller |
| 349 # is to be expecting a list in return, and not all callers do | 378 # is to be expecting a list in return, and not all callers do |
| 350 # because not all are working in list context. Also, for list | 379 # because not all are working in list context. Also, for list |
| 351 # expansions, there can be no other text besides the variable | 380 # expansions, there can be no other text besides the variable |
| 352 # expansion in the input. | 381 # expansion in the input. |
| 353 expand_to_list = '@' in match['type'] and input == match['replace'] | 382 expand_to_list = '@' in match['type'] and input == replacement |
| 354 | 383 |
| 355 # Capture these now so we can adjust them if necessary in the | |
| 356 # command substitution. | |
| 357 replace_start = match_group.start('replace') | |
| 358 replace_end = match_group.end('replace') | |
| 359 if run_command: | 384 if run_command: |
| 360 # Find the ending paren, and re-evaluate the contained string. | |
| 361 (c_start, c_end) = FindEnclosingBracketGroup(input[replace_start:]) | |
| 362 | |
| 363 # Adjust the replacement range to match the entire command | |
| 364 # found by FindEnclosingBracketGroup (since the variable_re | |
| 365 # probably doesn't match the entire command if it contained | |
| 366 # nested variables). | |
| 367 replace_end = replace_start + c_end | |
| 368 | |
| 369 # Strip out the command. | |
| 370 command_start = replace_start + c_start + 1 | |
| 371 command_end = replace_end - 1 | |
| 372 command = input[command_start:command_end] | |
| 373 # Recurse to expand variables in command executions. | |
| 374 command = ExpandVariables(command, is_late, variables, build_file) | |
| 375 | |
| 376 # Run the command in the build file's directory. | 385 # Run the command in the build file's directory. |
| 377 build_file_dir = os.path.dirname(build_file) | 386 build_file_dir = os.path.dirname(build_file) |
| 378 if build_file_dir == '': | 387 if build_file_dir == '': |
| 379 # If build_file is just a leaf filename indicating a file in the | 388 # If build_file is just a leaf filename indicating a file in the |
| 380 # current directory, build_file_dir might be an empty string. Set | 389 # current directory, build_file_dir might be an empty string. Set |
| 381 # it to None to signal to subprocess.Popen that it should run the | 390 # it to None to signal to subprocess.Popen that it should run the |
| 382 # command in the current directory. | 391 # command in the current directory. |
| 383 build_file_dir = None | 392 build_file_dir = None |
| 384 | 393 |
| 394 use_shell = True | |
| 385 if match['is_array']: | 395 if match['is_array']: |
| 386 command = eval(command) | 396 contents = eval(contents) |
| 397 use_shell = False | |
| 387 | 398 |
| 388 p = subprocess.Popen(command, shell=True, | 399 gyp.DebugOutput(gyp.DEBUG_VARIABLES, |
| 389 stdout=subprocess.PIPE, stderr=subprocess.PIPE, | 400 "Executing command '%s'" % contents) |
| 401 | |
| 402 | |
| 403 p = subprocess.Popen(contents, shell=use_shell, | |
| 404 stdout=subprocess.PIPE, | |
| 405 stderr=subprocess.PIPE, | |
| 406 stdin=subprocess.PIPE, | |
| 390 cwd=build_file_dir) | 407 cwd=build_file_dir) |
| 391 (p_stdout, p_stderr) = p.communicate() | 408 |
| 409 (p_stdout, p_stderr) = p.communicate('') | |
|
Greg Spencer
2009/07/30 22:46:07
Can you guys take a quick look at this? -- during
| |
| 392 | 410 |
| 393 if p.wait() != 0 or p_stderr: | 411 if p.wait() != 0 or p_stderr: |
| 394 sys.stderr.write(p_stderr) | 412 sys.stderr.write(p_stderr) |
| 395 # Simulate check_call behavior by reusing its exception. | 413 # Simulate check_call behavior, since check_call only exists |
| 396 raise subprocess.CalledProcessError(p.returncode, command) | 414 # in python 2.5 and later. |
| 415 raise Exception("Call to '%s' returned exit status %d." % | |
| 416 (contents, p.returncode)) | |
| 397 | 417 |
| 398 replacement = p_stdout.rstrip() | 418 replacement = p_stdout.rstrip() |
| 399 else: | 419 else: |
| 400 if not match['content'] in variables: | 420 if not contents in variables: |
| 401 raise KeyError, 'Undefined variable ' + match['content'] + \ | 421 raise KeyError, 'Undefined variable ' + contents + \ |
| 402 ' in ' + build_file | 422 ' in ' + build_file |
| 403 replacement = variables[match['content']] | 423 replacement = variables[contents] |
| 404 | 424 |
| 405 if isinstance(replacement, list): | 425 if isinstance(replacement, list): |
| 406 for item in replacement: | 426 for item in replacement: |
| 407 if not isinstance(item, str) and not isinstance(item, int): | 427 if not isinstance(item, str) and not isinstance(item, int): |
| 408 raise TypeError, 'Variable ' + match['content'] + \ | 428 raise TypeError, 'Variable ' + contents + \ |
| 409 ' must expand to a string or list of strings; ' + \ | 429 ' must expand to a string or list of strings; ' + \ |
| 410 'list contains a ' + \ | 430 'list contains a ' + \ |
| 411 item.__class__.__name__ | 431 item.__class__.__name__ |
| 412 # Run through the list and handle variable expansions in it. Since | 432 # Run through the list and handle variable expansions in it. Since |
| 413 # the list is guaranteed not to contain dicts, this won't do anything | 433 # the list is guaranteed not to contain dicts, this won't do anything |
| 414 # with conditions sections. | 434 # with conditions sections. |
| 415 # | |
| 416 # TODO(mark): I think this should be made more general: any time an | |
| 417 # expansion is done, if there are more expandable tokens left in the | |
| 418 # output, additional expansion phases should be done. It should not | |
| 419 # be effective solely for lists. | |
| 420 ProcessVariablesAndConditionsInList(replacement, is_late, variables, | 435 ProcessVariablesAndConditionsInList(replacement, is_late, variables, |
| 421 build_file) | 436 build_file) |
| 422 elif not isinstance(replacement, str) and \ | 437 elif not isinstance(replacement, str) and \ |
| 423 not isinstance(replacement, int): | 438 not isinstance(replacement, int): |
| 424 raise TypeError, 'Variable ' + match['content'] + \ | 439 raise TypeError, 'Variable ' + contents + \ |
| 425 ' must expand to a string or list of strings; ' + \ | 440 ' must expand to a string or list of strings; ' + \ |
| 426 'found a ' + replacement.__class__.__name__ | 441 'found a ' + replacement.__class__.__name__ |
| 427 | 442 |
| 428 if expand_to_list: | 443 if expand_to_list: |
| 429 # Expanding in list context. It's guaranteed that there's only one | 444 # Expanding in list context. It's guaranteed that there's only one |
| 430 # replacement to do in |input| and that it's this replacement. See | 445 # replacement to do in |input| and that it's this replacement. See |
| 431 # above. | 446 # above. |
| 432 if isinstance(replacement, list): | 447 if isinstance(replacement, list): |
| 433 # If it's already a list, make a copy. | 448 # If it's already a list, make a copy. |
| 434 output = replacement[:] | 449 output = replacement[:] |
| (...skipping 12 matching lines...) Expand all Loading... | |
| 447 # proper list-to-argument quoting rules on a specific | 462 # proper list-to-argument quoting rules on a specific |
| 448 # platform instead of just calling the POSIX encoding | 463 # platform instead of just calling the POSIX encoding |
| 449 # routine. | 464 # routine. |
| 450 encoded_replacement = gyp.common.EncodePOSIXShellList(replacement) | 465 encoded_replacement = gyp.common.EncodePOSIXShellList(replacement) |
| 451 else: | 466 else: |
| 452 encoded_replacement = replacement | 467 encoded_replacement = replacement |
| 453 | 468 |
| 454 output = output[:replace_start] + str(encoded_replacement) + \ | 469 output = output[:replace_start] + str(encoded_replacement) + \ |
| 455 output[replace_end:] | 470 output[replace_end:] |
| 456 | 471 |
| 472 gyp.DebugOutput(gyp.DEBUG_VARIABLES, | |
| 473 "Expanding '%s' to '%s'" % (input, output)) | |
| 457 return output | 474 return output |
| 458 | 475 |
| 459 | 476 |
| 460 def ProcessConditionsInDict(the_dict, is_late, variables, build_file): | 477 def ProcessConditionsInDict(the_dict, is_late, variables, build_file): |
| 461 # Process a 'conditions' or 'target_conditions' section in the_dict, | 478 # Process a 'conditions' or 'target_conditions' section in the_dict, |
| 462 # depending on is_late. If is_late is False, 'conditions' is used. | 479 # depending on is_late. If is_late is False, 'conditions' is used. |
| 463 # | 480 # |
| 464 # Each item in a conditions list consists of cond_expr, a string expression | 481 # Each item in a conditions list consists of cond_expr, a string expression |
| 465 # evaluated as the condition, and true_dict, a dict that will be merged into | 482 # evaluated as the condition, and true_dict, a dict that will be merged into |
| 466 # the_dict if cond_expr evaluates to true. Optionally, a third item, | 483 # the_dict if cond_expr evaluates to true. Optionally, a third item, |
| (...skipping 1201 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1668 # needed. Not all generators will need to use the rule_sources lists, but | 1685 # needed. Not all generators will need to use the rule_sources lists, but |
| 1669 # some may, and it seems best to build the list in a common spot. | 1686 # some may, and it seems best to build the list in a common spot. |
| 1670 for target in flat_list: | 1687 for target in flat_list: |
| 1671 target_dict = targets[target] | 1688 target_dict = targets[target] |
| 1672 ValidateRulesInTarget(target, target_dict, extra_sources_for_rules) | 1689 ValidateRulesInTarget(target, target_dict, extra_sources_for_rules) |
| 1673 | 1690 |
| 1674 # TODO(mark): Return |data| for now because the generator needs a list of | 1691 # TODO(mark): Return |data| for now because the generator needs a list of |
| 1675 # build files that came in. In the future, maybe it should just accept | 1692 # build files that came in. In the future, maybe it should just accept |
| 1676 # a list, and not the whole data dict. | 1693 # a list, and not the whole data dict. |
| 1677 return [flat_list, targets, data] | 1694 return [flat_list, targets, data] |
| OLD | NEW |