Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 # Copyright (c) 2012 Google Inc. All rights reserved. | 1 # Copyright (c) 2012 Google Inc. 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 from compiler.ast import Const | 5 from compiler.ast import Const |
| 6 from compiler.ast import Dict | 6 from compiler.ast import Dict |
| 7 from compiler.ast import Discard | 7 from compiler.ast import Discard |
| 8 from compiler.ast import List | 8 from compiler.ast import List |
| 9 from compiler.ast import Module | 9 from compiler.ast import Module |
| 10 from compiler.ast import Node | 10 from compiler.ast import Node |
| (...skipping 519 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 530 elif phase == PHASE_LATE: | 530 elif phase == PHASE_LATE: |
| 531 variable_re = late_variable_re | 531 variable_re = late_variable_re |
| 532 expansion_symbol = '>' | 532 expansion_symbol = '>' |
| 533 elif phase == PHASE_LATELATE: | 533 elif phase == PHASE_LATELATE: |
| 534 variable_re = latelate_variable_re | 534 variable_re = latelate_variable_re |
| 535 expansion_symbol = '^' | 535 expansion_symbol = '^' |
| 536 else: | 536 else: |
| 537 assert False | 537 assert False |
| 538 | 538 |
| 539 input_str = str(input) | 539 input_str = str(input) |
| 540 if IsStrCanonicalInt(input_str): | |
| 541 return int(input_str) | |
| 542 | |
| 540 # Do a quick scan to determine if an expensive regex search is warranted. | 543 # Do a quick scan to determine if an expensive regex search is warranted. |
| 541 if expansion_symbol in input_str: | 544 if expansion_symbol not in input_str: |
| 542 # Get the entire list of matches as a list of MatchObject instances. | 545 return input_str |
| 543 # (using findall here would return strings instead of MatchObjects). | 546 |
| 544 matches = [match for match in variable_re.finditer(input_str)] | 547 # Get the entire list of matches as a list of MatchObject instances. |
| 548 # (using findall here would return strings instead of MatchObjects). | |
| 549 matches = [match for match in variable_re.finditer(input_str)] | |
| 550 if not matches: | |
| 551 return input_str | |
| 552 | |
| 553 output = input_str | |
| 554 # Reverse the list of matches so that replacements are done right-to-left. | |
|
Nico
2012/05/28 21:54:50
Rietveld doesn't show it well, but from here on ev
| |
| 555 # That ensures that earlier replacements won't mess up the string in a | |
| 556 # way that causes later calls to find the earlier substituted text instead | |
| 557 # of what's intended for replacement. | |
| 558 matches.reverse() | |
| 559 for match_group in matches: | |
| 560 match = match_group.groupdict() | |
| 561 gyp.DebugOutput(gyp.DEBUG_VARIABLES, | |
| 562 "Matches: %s" % repr(match)) | |
| 563 # match['replace'] is the substring to look for, match['type'] | |
| 564 # is the character code for the replacement type (< > <! >! <| >| <@ | |
| 565 # >@ <!@ >!@), match['is_array'] contains a '[' for command | |
| 566 # arrays, and match['content'] is the name of the variable (< >) | |
| 567 # or command to run (<! >!). match['command_string'] is an optional | |
| 568 # command string. Currently, only 'pymod_do_main' is supported. | |
| 569 | |
| 570 # run_command is true if a ! variant is used. | |
| 571 run_command = '!' in match['type'] | |
| 572 command_string = match['command_string'] | |
| 573 | |
| 574 # file_list is true if a | variant is used. | |
| 575 file_list = '|' in match['type'] | |
| 576 | |
| 577 # Capture these now so we can adjust them later. | |
| 578 replace_start = match_group.start('replace') | |
| 579 replace_end = match_group.end('replace') | |
| 580 | |
| 581 # Find the ending paren, and re-evaluate the contained string. | |
| 582 (c_start, c_end) = FindEnclosingBracketGroup(input_str[replace_start:]) | |
| 583 | |
| 584 # Adjust the replacement range to match the entire command | |
| 585 # found by FindEnclosingBracketGroup (since the variable_re | |
| 586 # probably doesn't match the entire command if it contained | |
| 587 # nested variables). | |
| 588 replace_end = replace_start + c_end | |
| 589 | |
| 590 # Find the "real" replacement, matching the appropriate closing | |
| 591 # paren, and adjust the replacement start and end. | |
| 592 replacement = input_str[replace_start:replace_end] | |
| 593 | |
| 594 # Figure out what the contents of the variable parens are. | |
| 595 contents_start = replace_start + c_start + 1 | |
| 596 contents_end = replace_end - 1 | |
| 597 contents = input_str[contents_start:contents_end] | |
| 598 | |
| 599 # Do filter substitution now for <|(). | |
| 600 # Admittedly, this is different than the evaluation order in other | |
| 601 # contexts. However, since filtration has no chance to run on <|(), | |
| 602 # this seems like the only obvious way to give them access to filters. | |
| 603 if file_list: | |
| 604 processed_variables = copy.deepcopy(variables) | |
| 605 ProcessListFiltersInDict(contents, processed_variables) | |
| 606 # Recurse to expand variables in the contents | |
| 607 contents = ExpandVariables(contents, phase, | |
| 608 processed_variables, build_file) | |
| 609 else: | |
| 610 # Recurse to expand variables in the contents | |
| 611 contents = ExpandVariables(contents, phase, variables, build_file) | |
| 612 | |
| 613 # Strip off leading/trailing whitespace so that variable matches are | |
| 614 # simpler below (and because they are rarely needed). | |
| 615 contents = contents.strip() | |
| 616 | |
| 617 # expand_to_list is true if an @ variant is used. In that case, | |
| 618 # the expansion should result in a list. Note that the caller | |
| 619 # is to be expecting a list in return, and not all callers do | |
| 620 # because not all are working in list context. Also, for list | |
| 621 # expansions, there can be no other text besides the variable | |
| 622 # expansion in the input string. | |
| 623 expand_to_list = '@' in match['type'] and input_str == replacement | |
| 624 | |
| 625 if run_command or file_list: | |
| 626 # Find the build file's directory, so commands can be run or file lists | |
| 627 # generated relative to it. | |
| 628 build_file_dir = os.path.dirname(build_file) | |
| 629 if build_file_dir == '': | |
| 630 # If build_file is just a leaf filename indicating a file in the | |
| 631 # current directory, build_file_dir might be an empty string. Set | |
| 632 # it to None to signal to subprocess.Popen that it should run the | |
| 633 # command in the current directory. | |
| 634 build_file_dir = None | |
| 635 | |
| 636 # Support <|(listfile.txt ...) which generates a file | |
| 637 # containing items from a gyp list, generated at gyp time. | |
| 638 # This works around actions/rules which have more inputs than will | |
| 639 # fit on the command line. | |
| 640 if file_list: | |
| 641 if type(contents) == list: | |
| 642 contents_list = contents | |
| 643 else: | |
| 644 contents_list = contents.split(' ') | |
| 645 replacement = contents_list[0] | |
| 646 path = replacement | |
| 647 if not os.path.isabs(path): | |
| 648 path = os.path.join(build_file_dir, path) | |
| 649 f = gyp.common.WriteOnDiff(path) | |
| 650 for i in contents_list[1:]: | |
| 651 f.write('%s\n' % i) | |
| 652 f.close() | |
| 653 | |
| 654 elif run_command: | |
| 655 use_shell = True | |
| 656 if match['is_array']: | |
| 657 contents = eval(contents) | |
| 658 use_shell = False | |
| 659 | |
| 660 # Check for a cached value to avoid executing commands, or generating | |
| 661 # file lists more than once. | |
| 662 # TODO(http://code.google.com/p/gyp/issues/detail?id=112): It is | |
| 663 # possible that the command being invoked depends on the current | |
| 664 # directory. For that case the syntax needs to be extended so that the | |
| 665 # directory is also used in cache_key (it becomes a tuple). | |
| 666 # TODO(http://code.google.com/p/gyp/issues/detail?id=111): In theory, | |
| 667 # someone could author a set of GYP files where each time the command | |
| 668 # is invoked it produces different output by design. When the need | |
| 669 # arises, the syntax should be extended to support no caching off a | |
| 670 # command's output so it is run every time. | |
| 671 cache_key = str(contents) | |
| 672 cached_value = cached_command_results.get(cache_key, None) | |
| 673 if cached_value is None: | |
| 674 gyp.DebugOutput(gyp.DEBUG_VARIABLES, | |
| 675 "Executing command '%s' in directory '%s'" % | |
| 676 (contents,build_file_dir)) | |
| 677 | |
| 678 replacement = '' | |
| 679 | |
| 680 if command_string == 'pymod_do_main': | |
| 681 # <!pymod_do_main(modulename param eters) loads |modulename| as a | |
| 682 # python module and then calls that module's DoMain() function, | |
| 683 # passing ["param", "eters"] as a single list argument. For modules | |
| 684 # that don't load quickly, this can be faster than | |
| 685 # <!(python modulename param eters). Do this in |build_file_dir|. | |
| 686 oldwd = os.getcwd() # Python doesn't like os.open('.'): no fchdir. | |
| 687 os.chdir(build_file_dir) | |
| 688 | |
| 689 parsed_contents = shlex.split(contents) | |
| 690 py_module = __import__(parsed_contents[0]) | |
| 691 replacement = str(py_module.DoMain(parsed_contents[1:])).rstrip() | |
| 692 | |
| 693 os.chdir(oldwd) | |
| 694 assert replacement != None | |
| 695 elif command_string: | |
| 696 raise Exception("Unknown command string '%s' in '%s'." % | |
| 697 (command_string, contents)) | |
| 698 else: | |
| 699 # Fix up command with platform specific workarounds. | |
| 700 contents = FixupPlatformCommand(contents) | |
| 701 p = subprocess.Popen(contents, shell=use_shell, | |
| 702 stdout=subprocess.PIPE, | |
| 703 stderr=subprocess.PIPE, | |
| 704 stdin=subprocess.PIPE, | |
| 705 cwd=build_file_dir) | |
| 706 | |
| 707 p_stdout, p_stderr = p.communicate('') | |
| 708 | |
| 709 if p.wait() != 0 or p_stderr: | |
| 710 sys.stderr.write(p_stderr) | |
| 711 # Simulate check_call behavior, since check_call only exists | |
| 712 # in python 2.5 and later. | |
| 713 raise Exception("Call to '%s' returned exit status %d." % | |
| 714 (contents, p.returncode)) | |
| 715 replacement = p_stdout.rstrip() | |
| 716 | |
| 717 cached_command_results[cache_key] = replacement | |
| 718 else: | |
| 719 gyp.DebugOutput(gyp.DEBUG_VARIABLES, | |
| 720 "Had cache value for command '%s' in directory '%s'" % | |
| 721 (contents,build_file_dir)) | |
| 722 replacement = cached_value | |
| 723 | |
| 724 else: | |
| 725 if not contents in variables: | |
| 726 if contents[-1] in ['!', '/']: | |
| 727 # In order to allow cross-compiles (nacl) to happen more naturally, | |
| 728 # we will allow references to >(sources/) etc. to resolve to | |
| 729 # and empty list if undefined. This allows actions to: | |
| 730 # 'action!': [ | |
| 731 # '>@(_sources!)', | |
| 732 # ], | |
| 733 # 'action/': [ | |
| 734 # '>@(_sources/)', | |
| 735 # ], | |
| 736 replacement = [] | |
| 737 else: | |
| 738 raise KeyError, 'Undefined variable ' + contents + \ | |
| 739 ' in ' + build_file | |
| 740 else: | |
| 741 replacement = variables[contents] | |
| 742 | |
| 743 if isinstance(replacement, list): | |
| 744 for item in replacement: | |
| 745 if (not contents[-1] == '/' and | |
| 746 not isinstance(item, str) and not isinstance(item, int)): | |
| 747 raise TypeError, 'Variable ' + contents + \ | |
| 748 ' must expand to a string or list of strings; ' + \ | |
| 749 'list contains a ' + \ | |
| 750 item.__class__.__name__ | |
| 751 # Run through the list and handle variable expansions in it. Since | |
| 752 # the list is guaranteed not to contain dicts, this won't do anything | |
| 753 # with conditions sections. | |
| 754 ProcessVariablesAndConditionsInList(replacement, phase, variables, | |
| 755 build_file) | |
| 756 elif not isinstance(replacement, str) and \ | |
| 757 not isinstance(replacement, int): | |
| 758 raise TypeError, 'Variable ' + contents + \ | |
| 759 ' must expand to a string or list of strings; ' + \ | |
| 760 'found a ' + replacement.__class__.__name__ | |
| 761 | |
| 762 if expand_to_list: | |
| 763 # Expanding in list context. It's guaranteed that there's only one | |
| 764 # replacement to do in |input_str| and that it's this replacement. See | |
| 765 # above. | |
| 766 if isinstance(replacement, list): | |
| 767 # If it's already a list, make a copy. | |
| 768 output = replacement[:] | |
| 769 else: | |
| 770 # Split it the same way sh would split arguments. | |
| 771 output = shlex.split(str(replacement)) | |
| 772 else: | |
| 773 # Expanding in string context. | |
| 774 encoded_replacement = '' | |
| 775 if isinstance(replacement, list): | |
| 776 # When expanding a list into string context, turn the list items | |
| 777 # into a string in a way that will work with a subprocess call. | |
| 778 # | |
| 779 # TODO(mark): This isn't completely correct. This should | |
| 780 # call a generator-provided function that observes the | |
| 781 # proper list-to-argument quoting rules on a specific | |
| 782 # platform instead of just calling the POSIX encoding | |
| 783 # routine. | |
| 784 encoded_replacement = gyp.common.EncodePOSIXShellList(replacement) | |
| 785 else: | |
| 786 encoded_replacement = replacement | |
| 787 | |
| 788 output = output[:replace_start] + str(encoded_replacement) + \ | |
| 789 output[replace_end:] | |
| 790 # Prepare for the next match iteration. | |
| 791 input_str = output | |
| 792 | |
| 793 # Look for more matches now that we've replaced some, to deal with | |
| 794 # expanding local variables (variables defined in the same | |
| 795 # variables block as this one). | |
| 796 gyp.DebugOutput(gyp.DEBUG_VARIABLES, | |
| 797 "Found output %s, recursing." % repr(output)) | |
| 798 if isinstance(output, list): | |
| 799 if output and isinstance(output[0], list): | |
| 800 # Leave output alone if it's a list of lists. | |
| 801 # We don't want such lists to be stringified. | |
| 802 pass | |
| 803 else: | |
| 804 new_output = [] | |
| 805 for item in output: | |
| 806 new_output.append( | |
| 807 ExpandVariables(item, phase, variables, build_file)) | |
| 808 output = new_output | |
| 545 else: | 809 else: |
| 546 matches = None | 810 output = ExpandVariables(output, phase, variables, build_file) |
| 547 | |
| 548 output = input_str | |
| 549 if matches: | |
| 550 # Reverse the list of matches so that replacements are done right-to-left. | |
| 551 # That ensures that earlier replacements won't mess up the string in a | |
| 552 # way that causes later calls to find the earlier substituted text instead | |
| 553 # of what's intended for replacement. | |
| 554 matches.reverse() | |
| 555 for match_group in matches: | |
| 556 match = match_group.groupdict() | |
| 557 gyp.DebugOutput(gyp.DEBUG_VARIABLES, | |
| 558 "Matches: %s" % repr(match)) | |
| 559 # match['replace'] is the substring to look for, match['type'] | |
| 560 # is the character code for the replacement type (< > <! >! <| >| <@ | |
| 561 # >@ <!@ >!@), match['is_array'] contains a '[' for command | |
| 562 # arrays, and match['content'] is the name of the variable (< >) | |
| 563 # or command to run (<! >!). match['command_string'] is an optional | |
| 564 # command string. Currently, only 'pymod_do_main' is supported. | |
| 565 | |
| 566 # run_command is true if a ! variant is used. | |
| 567 run_command = '!' in match['type'] | |
| 568 command_string = match['command_string'] | |
| 569 | |
| 570 # file_list is true if a | variant is used. | |
| 571 file_list = '|' in match['type'] | |
| 572 | |
| 573 # Capture these now so we can adjust them later. | |
| 574 replace_start = match_group.start('replace') | |
| 575 replace_end = match_group.end('replace') | |
| 576 | |
| 577 # Find the ending paren, and re-evaluate the contained string. | |
| 578 (c_start, c_end) = FindEnclosingBracketGroup(input_str[replace_start:]) | |
| 579 | |
| 580 # Adjust the replacement range to match the entire command | |
| 581 # found by FindEnclosingBracketGroup (since the variable_re | |
| 582 # probably doesn't match the entire command if it contained | |
| 583 # nested variables). | |
| 584 replace_end = replace_start + c_end | |
| 585 | |
| 586 # Find the "real" replacement, matching the appropriate closing | |
| 587 # paren, and adjust the replacement start and end. | |
| 588 replacement = input_str[replace_start:replace_end] | |
| 589 | |
| 590 # Figure out what the contents of the variable parens are. | |
| 591 contents_start = replace_start + c_start + 1 | |
| 592 contents_end = replace_end - 1 | |
| 593 contents = input_str[contents_start:contents_end] | |
| 594 | |
| 595 # Do filter substitution now for <|(). | |
| 596 # Admittedly, this is different than the evaluation order in other | |
| 597 # contexts. However, since filtration has no chance to run on <|(), | |
| 598 # this seems like the only obvious way to give them access to filters. | |
| 599 if file_list: | |
| 600 processed_variables = copy.deepcopy(variables) | |
| 601 ProcessListFiltersInDict(contents, processed_variables) | |
| 602 # Recurse to expand variables in the contents | |
| 603 contents = ExpandVariables(contents, phase, | |
| 604 processed_variables, build_file) | |
| 605 else: | |
| 606 # Recurse to expand variables in the contents | |
| 607 contents = ExpandVariables(contents, phase, variables, build_file) | |
| 608 | |
| 609 # Strip off leading/trailing whitespace so that variable matches are | |
| 610 # simpler below (and because they are rarely needed). | |
| 611 contents = contents.strip() | |
| 612 | |
| 613 # expand_to_list is true if an @ variant is used. In that case, | |
| 614 # the expansion should result in a list. Note that the caller | |
| 615 # is to be expecting a list in return, and not all callers do | |
| 616 # because not all are working in list context. Also, for list | |
| 617 # expansions, there can be no other text besides the variable | |
| 618 # expansion in the input string. | |
| 619 expand_to_list = '@' in match['type'] and input_str == replacement | |
| 620 | |
| 621 if run_command or file_list: | |
| 622 # Find the build file's directory, so commands can be run or file lists | |
| 623 # generated relative to it. | |
| 624 build_file_dir = os.path.dirname(build_file) | |
| 625 if build_file_dir == '': | |
| 626 # If build_file is just a leaf filename indicating a file in the | |
| 627 # current directory, build_file_dir might be an empty string. Set | |
| 628 # it to None to signal to subprocess.Popen that it should run the | |
| 629 # command in the current directory. | |
| 630 build_file_dir = None | |
| 631 | |
| 632 # Support <|(listfile.txt ...) which generates a file | |
| 633 # containing items from a gyp list, generated at gyp time. | |
| 634 # This works around actions/rules which have more inputs than will | |
| 635 # fit on the command line. | |
| 636 if file_list: | |
| 637 if type(contents) == list: | |
| 638 contents_list = contents | |
| 639 else: | |
| 640 contents_list = contents.split(' ') | |
| 641 replacement = contents_list[0] | |
| 642 path = replacement | |
| 643 if not os.path.isabs(path): | |
| 644 path = os.path.join(build_file_dir, path) | |
| 645 f = gyp.common.WriteOnDiff(path) | |
| 646 for i in contents_list[1:]: | |
| 647 f.write('%s\n' % i) | |
| 648 f.close() | |
| 649 | |
| 650 elif run_command: | |
| 651 use_shell = True | |
| 652 if match['is_array']: | |
| 653 contents = eval(contents) | |
| 654 use_shell = False | |
| 655 | |
| 656 # Check for a cached value to avoid executing commands, or generating | |
| 657 # file lists more than once. | |
| 658 # TODO(http://code.google.com/p/gyp/issues/detail?id=112): It is | |
| 659 # possible that the command being invoked depends on the current | |
| 660 # directory. For that case the syntax needs to be extended so that the | |
| 661 # directory is also used in cache_key (it becomes a tuple). | |
| 662 # TODO(http://code.google.com/p/gyp/issues/detail?id=111): In theory, | |
| 663 # someone could author a set of GYP files where each time the command | |
| 664 # is invoked it produces different output by design. When the need | |
| 665 # arises, the syntax should be extended to support no caching off a | |
| 666 # command's output so it is run every time. | |
| 667 cache_key = str(contents) | |
| 668 cached_value = cached_command_results.get(cache_key, None) | |
| 669 if cached_value is None: | |
| 670 gyp.DebugOutput(gyp.DEBUG_VARIABLES, | |
| 671 "Executing command '%s' in directory '%s'" % | |
| 672 (contents,build_file_dir)) | |
| 673 | |
| 674 replacement = '' | |
| 675 | |
| 676 if command_string == 'pymod_do_main': | |
| 677 # <!pymod_do_main(modulename param eters) loads |modulename| as a | |
| 678 # python module and then calls that module's DoMain() function, | |
| 679 # passing ["param", "eters"] as a single list argument. For modules | |
| 680 # that don't load quickly, this can be faster than | |
| 681 # <!(python modulename param eters). Do this in |build_file_dir|. | |
| 682 oldwd = os.getcwd() # Python doesn't like os.open('.'): no fchdir. | |
| 683 os.chdir(build_file_dir) | |
| 684 | |
| 685 parsed_contents = shlex.split(contents) | |
| 686 py_module = __import__(parsed_contents[0]) | |
| 687 replacement = str(py_module.DoMain(parsed_contents[1:])).rstrip() | |
| 688 | |
| 689 os.chdir(oldwd) | |
| 690 assert replacement != None | |
| 691 elif command_string: | |
| 692 raise Exception("Unknown command string '%s' in '%s'." % | |
| 693 (command_string, contents)) | |
| 694 else: | |
| 695 # Fix up command with platform specific workarounds. | |
| 696 contents = FixupPlatformCommand(contents) | |
| 697 p = subprocess.Popen(contents, shell=use_shell, | |
| 698 stdout=subprocess.PIPE, | |
| 699 stderr=subprocess.PIPE, | |
| 700 stdin=subprocess.PIPE, | |
| 701 cwd=build_file_dir) | |
| 702 | |
| 703 p_stdout, p_stderr = p.communicate('') | |
| 704 | |
| 705 if p.wait() != 0 or p_stderr: | |
| 706 sys.stderr.write(p_stderr) | |
| 707 # Simulate check_call behavior, since check_call only exists | |
| 708 # in python 2.5 and later. | |
| 709 raise Exception("Call to '%s' returned exit status %d." % | |
| 710 (contents, p.returncode)) | |
| 711 replacement = p_stdout.rstrip() | |
| 712 | |
| 713 cached_command_results[cache_key] = replacement | |
| 714 else: | |
| 715 gyp.DebugOutput(gyp.DEBUG_VARIABLES, | |
| 716 "Had cache value for command '%s' in directory '%s'" % | |
| 717 (contents,build_file_dir)) | |
| 718 replacement = cached_value | |
| 719 | |
| 720 else: | |
| 721 if not contents in variables: | |
| 722 if contents[-1] in ['!', '/']: | |
| 723 # In order to allow cross-compiles (nacl) to happen more naturally, | |
| 724 # we will allow references to >(sources/) etc. to resolve to | |
| 725 # and empty list if undefined. This allows actions to: | |
| 726 # 'action!': [ | |
| 727 # '>@(_sources!)', | |
| 728 # ], | |
| 729 # 'action/': [ | |
| 730 # '>@(_sources/)', | |
| 731 # ], | |
| 732 replacement = [] | |
| 733 else: | |
| 734 raise KeyError, 'Undefined variable ' + contents + \ | |
| 735 ' in ' + build_file | |
| 736 else: | |
| 737 replacement = variables[contents] | |
| 738 | |
| 739 if isinstance(replacement, list): | |
| 740 for item in replacement: | |
| 741 if (not contents[-1] == '/' and | |
| 742 not isinstance(item, str) and not isinstance(item, int)): | |
| 743 raise TypeError, 'Variable ' + contents + \ | |
| 744 ' must expand to a string or list of strings; ' + \ | |
| 745 'list contains a ' + \ | |
| 746 item.__class__.__name__ | |
| 747 # Run through the list and handle variable expansions in it. Since | |
| 748 # the list is guaranteed not to contain dicts, this won't do anything | |
| 749 # with conditions sections. | |
| 750 ProcessVariablesAndConditionsInList(replacement, phase, variables, | |
| 751 build_file) | |
| 752 elif not isinstance(replacement, str) and \ | |
| 753 not isinstance(replacement, int): | |
| 754 raise TypeError, 'Variable ' + contents + \ | |
| 755 ' must expand to a string or list of strings; ' + \ | |
| 756 'found a ' + replacement.__class__.__name__ | |
| 757 | |
| 758 if expand_to_list: | |
| 759 # Expanding in list context. It's guaranteed that there's only one | |
| 760 # replacement to do in |input_str| and that it's this replacement. See | |
| 761 # above. | |
| 762 if isinstance(replacement, list): | |
| 763 # If it's already a list, make a copy. | |
| 764 output = replacement[:] | |
| 765 else: | |
| 766 # Split it the same way sh would split arguments. | |
| 767 output = shlex.split(str(replacement)) | |
| 768 else: | |
| 769 # Expanding in string context. | |
| 770 encoded_replacement = '' | |
| 771 if isinstance(replacement, list): | |
| 772 # When expanding a list into string context, turn the list items | |
| 773 # into a string in a way that will work with a subprocess call. | |
| 774 # | |
| 775 # TODO(mark): This isn't completely correct. This should | |
| 776 # call a generator-provided function that observes the | |
| 777 # proper list-to-argument quoting rules on a specific | |
| 778 # platform instead of just calling the POSIX encoding | |
| 779 # routine. | |
| 780 encoded_replacement = gyp.common.EncodePOSIXShellList(replacement) | |
| 781 else: | |
| 782 encoded_replacement = replacement | |
| 783 | |
| 784 output = output[:replace_start] + str(encoded_replacement) + \ | |
| 785 output[replace_end:] | |
| 786 # Prepare for the next match iteration. | |
| 787 input_str = output | |
| 788 | |
| 789 # Look for more matches now that we've replaced some, to deal with | |
| 790 # expanding local variables (variables defined in the same | |
| 791 # variables block as this one). | |
| 792 gyp.DebugOutput(gyp.DEBUG_VARIABLES, | |
| 793 "Found output %s, recursing." % repr(output)) | |
| 794 if isinstance(output, list): | |
| 795 if output and isinstance(output[0], list): | |
| 796 # Leave output alone if it's a list of lists. | |
| 797 # We don't want such lists to be stringified. | |
| 798 pass | |
| 799 else: | |
| 800 new_output = [] | |
| 801 for item in output: | |
| 802 new_output.append( | |
| 803 ExpandVariables(item, phase, variables, build_file)) | |
| 804 output = new_output | |
| 805 else: | |
| 806 output = ExpandVariables(output, phase, variables, build_file) | |
| 807 | 811 |
| 808 # Convert all strings that are canonically-represented integers into integers. | 812 # Convert all strings that are canonically-represented integers into integers. |
| 809 if isinstance(output, list): | 813 if isinstance(output, list): |
| 810 for index in xrange(0, len(output)): | 814 for index in xrange(0, len(output)): |
| 811 if IsStrCanonicalInt(output[index]): | 815 if IsStrCanonicalInt(output[index]): |
| 812 output[index] = int(output[index]) | 816 output[index] = int(output[index]) |
| 813 elif IsStrCanonicalInt(output): | 817 elif IsStrCanonicalInt(output): |
| 814 output = int(output) | 818 output = int(output) |
| 815 | 819 |
| 816 return output | 820 return output |
| (...skipping 1657 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 2474 ValidateRunAsInTarget(target, target_dict, build_file) | 2478 ValidateRunAsInTarget(target, target_dict, build_file) |
| 2475 ValidateActionsInTarget(target, target_dict, build_file) | 2479 ValidateActionsInTarget(target, target_dict, build_file) |
| 2476 | 2480 |
| 2477 # Generators might not expect ints. Turn them into strs. | 2481 # Generators might not expect ints. Turn them into strs. |
| 2478 TurnIntIntoStrInDict(data) | 2482 TurnIntIntoStrInDict(data) |
| 2479 | 2483 |
| 2480 # TODO(mark): Return |data| for now because the generator needs a list of | 2484 # TODO(mark): Return |data| for now because the generator needs a list of |
| 2481 # build files that came in. In the future, maybe it should just accept | 2485 # build files that came in. In the future, maybe it should just accept |
| 2482 # a list, and not the whole data dict. | 2486 # a list, and not the whole data dict. |
| 2483 return [flat_list, targets, data] | 2487 return [flat_list, targets, data] |
| OLD | NEW |