OLD | NEW |
1 # Copyright 2014 Google Inc. All Rights Reserved. | 1 # Copyright 2014 Google Inc. All Rights Reserved. |
2 # | 2 # |
3 # Licensed under the Apache License, Version 2.0 (the "License"); | 3 # Licensed under the Apache License, Version 2.0 (the "License"); |
4 # you may not use this file except in compliance with the License. | 4 # you may not use this file except in compliance with the License. |
5 # You may obtain a copy of the License at | 5 # You may obtain a copy of the License at |
6 # | 6 # |
7 # http://www.apache.org/licenses/LICENSE-2.0 | 7 # http://www.apache.org/licenses/LICENSE-2.0 |
8 # | 8 # |
9 # Unless required by applicable law or agreed to in writing, software | 9 # Unless required by applicable law or agreed to in writing, software |
10 # distributed under the License is distributed on an "AS IS" BASIS, | 10 # distributed under the License is distributed on an "AS IS" BASIS, |
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
47 import glob | 47 import glob |
48 import hashlib | 48 import hashlib |
49 import logging | 49 import logging |
50 import optparse | 50 import optparse |
51 import os | 51 import os |
52 import random | 52 import random |
53 import re | 53 import re |
54 import subprocess | 54 import subprocess |
55 import threading | 55 import threading |
56 | 56 |
| 57 import deps_utils |
57 | 58 |
58 _LOGGER = logging.getLogger(os.path.basename(__file__)) | 59 _LOGGER = logging.getLogger(os.path.basename(__file__)) |
59 | 60 |
60 | 61 |
61 # Matches a SHA1 hash used as a git revision. | 62 # Matches a SHA1 hash used as a git revision. |
62 _GIT_SHA1_RE = re.compile('^[A-Fa-f0-9]{40}$') | 63 _GIT_SHA1_RE = re.compile('^[A-Fa-f0-9]{40}$') |
63 | 64 |
64 | 65 |
65 def _ParseCommandLine(): | 66 def _ParseCommandLine(): |
66 """Parses the command-line and returns an options structure.""" | 67 """Parses the command-line and returns an options structure.""" |
(...skipping 556 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
623 'safesync_url': '', | 624 'safesync_url': '', |
624 } | 625 } |
625 ] | 626 ] |
626 solutions = 'solutions=%s' % solutions.__repr__() | 627 solutions = 'solutions=%s' % solutions.__repr__() |
627 _WriteIfChanged(gclient_file, solutions, options.dry_run) | 628 _WriteIfChanged(gclient_file, solutions, options.dry_run) |
628 | 629 |
629 # Invoke 'gclient' on the sub-repository. | 630 # Invoke 'gclient' on the sub-repository. |
630 _Shell('gclient', 'sync', cwd=repo.checkout_dir, dry_run=options.dry_run) | 631 _Shell('gclient', 'sync', cwd=repo.checkout_dir, dry_run=options.dry_run) |
631 | 632 |
632 | 633 |
633 def _FindGlobalVariableInAstTree(tree, name, functions=None): | |
634 """Finds and evaluates to global assignment of the variables |name| in the | |
635 AST |tree|. Will allow the evaluations of some functions as defined in | |
636 |functions|. | |
637 """ | |
638 if functions is None: | |
639 functions = {} | |
640 | |
641 class FunctionEvaluator(ast.NodeTransformer): | |
642 """A tree transformer that evaluates permitted functions.""" | |
643 | |
644 def visit_BinOp(self, binop_node): | |
645 """Is called for BinOp nodes. We only support string additions.""" | |
646 if type(binop_node.op) != ast.Add: | |
647 return binop_node | |
648 left = ast.literal_eval(self.visit(binop_node.left)) | |
649 right = ast.literal_eval(self.visit(binop_node.right)) | |
650 value = left + right | |
651 new_node = ast.Str(s=value) | |
652 new_node = ast.copy_location(new_node, binop_node) | |
653 return new_node | |
654 | |
655 def visit_Call(self, call_node): | |
656 """Evaluates function calls that return a single string as output.""" | |
657 func_name = call_node.func.id | |
658 if func_name not in functions: | |
659 return call_node | |
660 func = functions[func_name] | |
661 | |
662 # Evaluate the arguments. We don't care about starargs, keywords or | |
663 # kwargs. | |
664 args = [ast.literal_eval(self.visit(arg)) for arg in | |
665 call_node.args] | |
666 | |
667 # Now evaluate the function. | |
668 value = func(*args) | |
669 new_node = ast.Str(s=value) | |
670 new_node = ast.copy_location(new_node, call_node) | |
671 return new_node | |
672 | |
673 # Look for assignment nodes. | |
674 for node in tree.body: | |
675 if type(node) != ast.Assign: | |
676 continue | |
677 # Look for assignment in the 'store' context, to a variable with | |
678 # the given name. | |
679 for target in node.targets: | |
680 if type(target) != ast.Name: | |
681 continue | |
682 if type(target.ctx) != ast.Store: | |
683 continue | |
684 if target.id == name: | |
685 value = FunctionEvaluator().visit(node.value) | |
686 value = ast.fix_missing_locations(value) | |
687 value = ast.literal_eval(value) | |
688 return value | |
689 | |
690 | |
691 def _ParseDepsFile(path): | |
692 """Parsed a DEPS-like file at the given |path|.""" | |
693 # Utility function for performing variable expansions. | |
694 vars_dict = {} | |
695 def _Var(s): | |
696 return vars_dict[s] | |
697 | |
698 contents = open(path, 'rb').read() | |
699 tree = ast.parse(contents, path) | |
700 vars_dict = _FindGlobalVariableInAstTree(tree, 'vars') | |
701 deps_dict = _FindGlobalVariableInAstTree( | |
702 tree, 'deps', functions={'Var': _Var}) | |
703 return deps_dict | |
704 | 634 |
705 | 635 |
706 def _RemoveFile(options, path): | 636 def _RemoveFile(options, path): |
707 """Removes the provided file. If it doesn't exist, raises an Exception.""" | 637 """Removes the provided file. If it doesn't exist, raises an Exception.""" |
708 _LOGGER.debug('Removing file: %s', path) | 638 _LOGGER.debug('Removing file: %s', path) |
709 if not os.path.isfile(path): | 639 if not os.path.isfile(path): |
710 raise Exception('Path does not exist: %s' % path) | 640 raise Exception('Path does not exist: %s' % path) |
711 | 641 |
712 if not options.dry_run: | 642 if not options.dry_run: |
713 os.remove(path) | 643 os.remove(path) |
(...skipping 153 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
867 | 797 |
868 # Read junctions that have been written in previous runs. | 798 # Read junctions that have been written in previous runs. |
869 state_path = _GetJunctionStatePath(options) | 799 state_path = _GetJunctionStatePath(options) |
870 old_junctions = _ReadJunctions(options) | 800 old_junctions = _ReadJunctions(options) |
871 | 801 |
872 # Parse each deps file in order, and extract the dependencies, looking for | 802 # Parse each deps file in order, and extract the dependencies, looking for |
873 # conflicts in the output directories. | 803 # conflicts in the output directories. |
874 output_dirs = {} | 804 output_dirs = {} |
875 all_deps = [] | 805 all_deps = [] |
876 for deps_file in args: | 806 for deps_file in args: |
877 deps = _ParseDepsFile(deps_file) | 807 deps = deps_utils.ParseDepsFile(deps_file) |
878 for key, value in deps.iteritems(): | 808 for key, value in deps.iteritems(): |
879 repo_options = _ParseRepoOptions( | 809 repo_options = _ParseRepoOptions( |
880 options.cache_dir, options.output_dir, deps_file, key, value) | 810 options.cache_dir, options.output_dir, deps_file, key, value) |
881 if repo_options.output_dir in output_dirs: | 811 if repo_options.output_dir in output_dirs: |
882 other_repo_options = output_dirs[repo_options.output_dir] | 812 other_repo_options = output_dirs[repo_options.output_dir] |
883 _LOGGER.error('Conflicting output directory: %s', | 813 _LOGGER.error('Conflicting output directory: %s', |
884 repo_options.output_dir) | 814 repo_options.output_dir) |
885 _LOGGER.error('First specified in file: %s', | 815 _LOGGER.error('First specified in file: %s', |
886 other_repo_options.deps_file) | 816 other_repo_options.deps_file) |
887 _LOGGER.error('And then specified in file: %s', repo_options.deps_file) | 817 _LOGGER.error('And then specified in file: %s', repo_options.deps_file) |
(...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
944 continue | 874 continue |
945 if not checkout_dirs[repo.checkout_dir] and not options.force: | 875 if not checkout_dirs[repo.checkout_dir] and not options.force: |
946 continue | 876 continue |
947 _RecurseRepository(options, repo) | 877 _RecurseRepository(options, repo) |
948 | 878 |
949 return | 879 return |
950 | 880 |
951 | 881 |
952 if __name__ == '__main__': | 882 if __name__ == '__main__': |
953 main() | 883 main() |
OLD | NEW |