Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(55)

Side by Side Diff: pylib/gyp/input.py

Issue 159605: This changes variable expansion so that it is recursive (Closed) Base URL: http://gyp.googlecode.com/svn/trunk/
Patch Set: '' Created 11 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « pylib/gyp/__init__.py ('k') | tests/run_tests.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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
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]
OLDNEW
« no previous file with comments | « pylib/gyp/__init__.py ('k') | tests/run_tests.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698