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

Side by Side Diff: gclient.py

Issue 10127004: Add the ability to specify a target_os for gclient solutions (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/tools/depot_tools/
Patch Set: Created 8 years, 8 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 | « no previous file | tests/gclient_test.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/env python 1 #!/usr/bin/env python
2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be 3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file. 4 # found in the LICENSE file.
5 5
6 """Meta checkout manager supporting both Subversion and GIT. 6 """Meta checkout manager supporting both Subversion and GIT.
7 7
8 Files 8 Files
9 .gclient : Current client configuration, written by 'config' command. 9 .gclient : Current client configuration, written by 'config' command.
10 Format is a Python script defining 'solutions', a list whose 10 Format is a Python script defining 'solutions', a list whose
(...skipping 29 matching lines...) Expand all
40 the current Python interpreter (sys.executable) will be used 40 the current Python interpreter (sys.executable) will be used
41 to run the command. If the list contains string "$matching_files" 41 to run the command. If the list contains string "$matching_files"
42 it will be removed from the list and the list will be extended 42 it will be removed from the list and the list will be extended
43 by the list of matching files. 43 by the list of matching files.
44 44
45 Example: 45 Example:
46 hooks = [ 46 hooks = [
47 { "pattern": "\\.(gif|jpe?g|pr0n|png)$", 47 { "pattern": "\\.(gif|jpe?g|pr0n|png)$",
48 "action": ["python", "image_indexer.py", "--all"]}, 48 "action": ["python", "image_indexer.py", "--all"]},
49 ] 49 ]
50
51 Specifying a target OS
52 An optional key named "target_os" may be added to any solution to specify
53 an additional operating system that should be considered when processing
54 the deps_os dict of a DEPS file.
50 """ 55 """
51 56
52 __version__ = "0.6.4" 57 __version__ = "0.6.4"
53 58
54 import copy 59 import copy
55 import logging 60 import logging
56 import optparse 61 import optparse
57 import os 62 import os
58 import posixpath 63 import posixpath
59 import pprint 64 import pprint
(...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after
136 return self._custom_vars[var_name] 141 return self._custom_vars[var_name]
137 elif var_name in self._local_scope.get("vars", {}): 142 elif var_name in self._local_scope.get("vars", {}):
138 return self._local_scope["vars"][var_name] 143 return self._local_scope["vars"][var_name]
139 raise gclient_utils.Error("Var is not defined: %s" % var_name) 144 raise gclient_utils.Error("Var is not defined: %s" % var_name)
140 145
141 146
142 class DependencySettings(GClientKeywords): 147 class DependencySettings(GClientKeywords):
143 """Immutable configuration settings.""" 148 """Immutable configuration settings."""
144 def __init__( 149 def __init__(
145 self, parent, url, safesync_url, managed, custom_deps, custom_vars, 150 self, parent, url, safesync_url, managed, custom_deps, custom_vars,
146 deps_file, should_process): 151 deps_file, target_os, should_process):
M-A Ruel 2012/04/19 13:29:03 It also doesn't make sense to make it per Dependen
147 GClientKeywords.__init__(self) 152 GClientKeywords.__init__(self)
148 153
149 # These are not mutable: 154 # These are not mutable:
150 self._parent = parent 155 self._parent = parent
151 self._safesync_url = safesync_url 156 self._safesync_url = safesync_url
152 self._deps_file = deps_file 157 self._deps_file = deps_file
153 self._url = url 158 self._url = url
159 self._target_os = target_os
154 # 'managed' determines whether or not this dependency is synced/updated by 160 # 'managed' determines whether or not this dependency is synced/updated by
155 # gclient after gclient checks it out initially. The difference between 161 # gclient after gclient checks it out initially. The difference between
156 # 'managed' and 'should_process' is that the user specifies 'managed' via 162 # 'managed' and 'should_process' is that the user specifies 'managed' via
157 # the --unmanaged command-line flag or a .gclient config, where 163 # the --unmanaged command-line flag or a .gclient config, where
158 # 'should_process' is dynamically set by gclient if it goes over its 164 # 'should_process' is dynamically set by gclient if it goes over its
159 # recursion limit and controls gclient's behavior so it does not misbehave. 165 # recursion limit and controls gclient's behavior so it does not misbehave.
160 self._managed = managed 166 self._managed = managed
161 self._should_process = should_process 167 self._should_process = should_process
162 168
163 # These are only set in .gclient and not in DEPS files. 169 # These are only set in .gclient and not in DEPS files.
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after
213 219
214 @property 220 @property
215 def custom_deps(self): 221 def custom_deps(self):
216 return self._custom_deps.copy() 222 return self._custom_deps.copy()
217 223
218 @property 224 @property
219 def url(self): 225 def url(self):
220 return self._url 226 return self._url
221 227
222 @property 228 @property
229 def target_os(self):
230 return self._target_os
231
232 @property
223 def recursion_limit(self): 233 def recursion_limit(self):
224 """Returns > 0 if this dependency is not too recursed to be processed.""" 234 """Returns > 0 if this dependency is not too recursed to be processed."""
225 return max(self.parent.recursion_limit - 1, 0) 235 return max(self.parent.recursion_limit - 1, 0)
226 236
227 def get_custom_deps(self, name, url): 237 def get_custom_deps(self, name, url):
228 """Returns a custom deps if applicable.""" 238 """Returns a custom deps if applicable."""
229 if self.parent: 239 if self.parent:
230 url = self.parent.get_custom_deps(name, url) 240 url = self.parent.get_custom_deps(name, url)
231 # None is a valid return value to disable a dependency. 241 # None is a valid return value to disable a dependency.
232 return self.custom_deps.get(name, url) 242 return self.custom_deps.get(name, url)
233 243
234 244
235 class Dependency(gclient_utils.WorkItem, DependencySettings): 245 class Dependency(gclient_utils.WorkItem, DependencySettings):
236 """Object that represents a dependency checkout.""" 246 """Object that represents a dependency checkout."""
237 247
238 def __init__(self, parent, name, url, safesync_url, managed, custom_deps, 248 def __init__(self, parent, name, url, safesync_url, managed, custom_deps,
239 custom_vars, deps_file, should_process): 249 custom_vars, deps_file, target_os, should_process):
240 gclient_utils.WorkItem.__init__(self, name) 250 gclient_utils.WorkItem.__init__(self, name)
241 DependencySettings.__init__( 251 DependencySettings.__init__(
242 self, parent, url, safesync_url, managed, custom_deps, custom_vars, 252 self, parent, url, safesync_url, managed, custom_deps, custom_vars,
243 deps_file, should_process) 253 deps_file, target_os, should_process)
244 254
245 # This is in both .gclient and DEPS files: 255 # This is in both .gclient and DEPS files:
246 self._deps_hooks = [] 256 self._deps_hooks = []
247 257
248 # Calculates properties: 258 # Calculates properties:
249 self._parsed_url = None 259 self._parsed_url = None
250 self._dependencies = [] 260 self._dependencies = []
251 # A cache of the files affected by the current operation, necessary for 261 # A cache of the files affected by the current operation, necessary for
252 # hooks. 262 # hooks.
253 self._file_list = [] 263 self._file_list = []
(...skipping 169 matching lines...) Expand 10 before | Expand all | Expand 10 after
423 # Eval the content. 433 # Eval the content.
424 try: 434 try:
425 exec(deps_content, global_scope, local_scope) 435 exec(deps_content, global_scope, local_scope)
426 except SyntaxError, e: 436 except SyntaxError, e:
427 gclient_utils.SyntaxErrorToError(filepath, e) 437 gclient_utils.SyntaxErrorToError(filepath, e)
428 deps = local_scope.get('deps', {}) 438 deps = local_scope.get('deps', {})
429 # load os specific dependencies if defined. these dependencies may 439 # load os specific dependencies if defined. these dependencies may
430 # override or extend the values defined by the 'deps' member. 440 # override or extend the values defined by the 'deps' member.
431 if 'deps_os' in local_scope: 441 if 'deps_os' in local_scope:
432 enforced_os = self.root.enforced_os 442 enforced_os = self.root.enforced_os
443 if self.target_os and self.target_os not in enforced_os:
444 enforced_os = enforced_os + tuple([self.target_os])
445
433 for deps_os_key in enforced_os: 446 for deps_os_key in enforced_os:
434 os_deps = local_scope['deps_os'].get(deps_os_key, {}) 447 os_deps = local_scope['deps_os'].get(deps_os_key, {})
435 if len(enforced_os) > 1: 448 if len(enforced_os) > 1:
436 # Ignore any conflict when including deps for more than one 449 # Ignore any conflict when including deps for more than one
437 # platform, so we collect the broadest set of dependencies 450 # platform, so we collect the broadest set of dependencies
438 # available. We may end up with the wrong revision of something for 451 # available. We may end up with the wrong revision of something for
439 # our platform, but this is the best we can do. 452 # our platform, but this is the best we can do.
440 deps.update([x for x in os_deps.items() if not x[0] in deps]) 453 deps.update([x for x in os_deps.items() if not x[0] in deps])
441 else: 454 else:
442 deps.update(os_deps) 455 deps.update(os_deps)
(...skipping 15 matching lines...) Expand all
458 # dependency local path. 471 # dependency local path.
459 rel_deps[os.path.normpath(os.path.join(self.name, d))] = url 472 rel_deps[os.path.normpath(os.path.join(self.name, d))] = url
460 deps = rel_deps 473 deps = rel_deps
461 474
462 # Convert the deps into real Dependency. 475 # Convert the deps into real Dependency.
463 deps_to_add = [] 476 deps_to_add = []
464 for name, url in deps.iteritems(): 477 for name, url in deps.iteritems():
465 should_process = self.recursion_limit and self.should_process 478 should_process = self.recursion_limit and self.should_process
466 deps_to_add.append(Dependency( 479 deps_to_add.append(Dependency(
467 self, name, url, None, None, None, None, 480 self, name, url, None, None, None, None,
468 self.deps_file, should_process)) 481 self.deps_file, self.target_os, should_process))
469 deps_to_add.sort(key=lambda x: x.name) 482 deps_to_add.sort(key=lambda x: x.name)
470 self.add_dependencies_and_close(deps_to_add, local_scope.get('hooks', [])) 483 self.add_dependencies_and_close(deps_to_add, local_scope.get('hooks', []))
471 logging.info('ParseDepsFile(%s) done' % self.name) 484 logging.info('ParseDepsFile(%s) done' % self.name)
472 485
473 def add_dependencies_and_close(self, deps_to_add, hooks): 486 def add_dependencies_and_close(self, deps_to_add, hooks):
474 """Adds the dependencies, hooks and mark the parsing as done.""" 487 """Adds the dependencies, hooks and mark the parsing as done."""
475 for dep in deps_to_add: 488 for dep in deps_to_add:
476 if dep.verify_validity(): 489 if dep.verify_validity():
477 self.add_dependency(dep) 490 self.add_dependency(dep)
478 self._mark_as_parsed(hooks) 491 self._mark_as_parsed(hooks)
(...skipping 257 matching lines...) Expand 10 before | Expand all | Expand 10 after
736 def file_list_and_children(self): 749 def file_list_and_children(self):
737 result = list(self.file_list) 750 result = list(self.file_list)
738 for d in self.dependencies: 751 for d in self.dependencies:
739 result.extend(d.file_list_and_children) 752 result.extend(d.file_list_and_children)
740 return tuple(result) 753 return tuple(result)
741 754
742 def __str__(self): 755 def __str__(self):
743 out = [] 756 out = []
744 for i in ('name', 'url', 'parsed_url', 'safesync_url', 'custom_deps', 757 for i in ('name', 'url', 'parsed_url', 'safesync_url', 'custom_deps',
745 'custom_vars', 'deps_hooks', 'file_list', 'should_process', 758 'custom_vars', 'deps_hooks', 'file_list', 'should_process',
746 'processed', 'hooks_ran', 'deps_parsed', 'requirements'): 759 'target_os', 'processed', 'hooks_ran', 'deps_parsed',
760 'requirements'):
747 # First try the native property if it exists. 761 # First try the native property if it exists.
748 if hasattr(self, '_' + i): 762 if hasattr(self, '_' + i):
749 value = getattr(self, '_' + i, False) 763 value = getattr(self, '_' + i, False)
750 else: 764 else:
751 value = getattr(self, i, False) 765 value = getattr(self, i, False)
752 if value: 766 if value:
753 out.append('%s: %s' % (i, value)) 767 out.append('%s: %s' % (i, value))
754 768
755 for d in self.dependencies: 769 for d in self.dependencies:
756 out.extend([' ' + x for x in str(d).splitlines()]) 770 out.extend([' ' + x for x in str(d).splitlines()])
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after
814 # Snapshot generated with gclient revinfo --snapshot 828 # Snapshot generated with gclient revinfo --snapshot
815 solutions = [ 829 solutions = [
816 %(solution_list)s] 830 %(solution_list)s]
817 """) 831 """)
818 832
819 def __init__(self, root_dir, options): 833 def __init__(self, root_dir, options):
820 # Do not change previous behavior. Only solution level and immediate DEPS 834 # Do not change previous behavior. Only solution level and immediate DEPS
821 # are processed. 835 # are processed.
822 self._recursion_limit = 2 836 self._recursion_limit = 2
823 Dependency.__init__(self, None, None, None, None, True, None, None, 837 Dependency.__init__(self, None, None, None, None, True, None, None,
824 'unused', True) 838 'unused', None, True)
825 self._options = options 839 self._options = options
826 if options.deps_os: 840 if options.deps_os:
827 enforced_os = options.deps_os.split(',') 841 enforced_os = options.deps_os.split(',')
828 else: 842 else:
829 enforced_os = [self.DEPS_OS_CHOICES.get(sys.platform, 'unix')] 843 enforced_os = [self.DEPS_OS_CHOICES.get(sys.platform, 'unix')]
830 if 'all' in enforced_os: 844 if 'all' in enforced_os:
831 enforced_os = self.DEPS_OS_CHOICES.itervalues() 845 enforced_os = self.DEPS_OS_CHOICES.itervalues()
832 self._enforced_os = tuple(set(enforced_os)) 846 self._enforced_os = tuple(set(enforced_os))
M-A Ruel 2012/04/19 13:29:03 enforced_os already does what you want. It is used
833 self._root_dir = root_dir 847 self._root_dir = root_dir
834 self.config_content = None 848 self.config_content = None
835 849
836 def SetConfig(self, content): 850 def SetConfig(self, content):
837 assert not self.dependencies 851 assert not self.dependencies
838 config_dict = {} 852 config_dict = {}
839 self.config_content = content 853 self.config_content = content
840 try: 854 try:
841 exec(content, config_dict) 855 exec(content, config_dict)
842 except SyntaxError, e: 856 except SyntaxError, e:
843 gclient_utils.SyntaxErrorToError('.gclient', e) 857 gclient_utils.SyntaxErrorToError('.gclient', e)
844 858
845 deps_to_add = [] 859 deps_to_add = []
846 for s in config_dict.get('solutions', []): 860 for s in config_dict.get('solutions', []):
847 try: 861 try:
848 deps_to_add.append(Dependency( 862 deps_to_add.append(Dependency(
849 self, s['name'], s['url'], 863 self, s['name'], s['url'],
850 s.get('safesync_url', None), 864 s.get('safesync_url', None),
851 s.get('managed', True), 865 s.get('managed', True),
852 s.get('custom_deps', {}), 866 s.get('custom_deps', {}),
853 s.get('custom_vars', {}), 867 s.get('custom_vars', {}),
854 s.get('deps_file', 'DEPS'), 868 s.get('deps_file', 'DEPS'),
869 s.get('target_os', None),
M-A Ruel 2012/04/19 13:29:03 enforced_os doesn't make sense to be per solution.
855 True)) 870 True))
856 except KeyError: 871 except KeyError:
857 raise gclient_utils.Error('Invalid .gclient file. Solution is ' 872 raise gclient_utils.Error('Invalid .gclient file. Solution is '
858 'incomplete: %s' % s) 873 'incomplete: %s' % s)
859 self.add_dependencies_and_close(deps_to_add, config_dict.get('hooks', [])) 874 self.add_dependencies_and_close(deps_to_add, config_dict.get('hooks', []))
860 logging.info('SetConfig() done') 875 logging.info('SetConfig() done')
861 876
862 def SaveConfig(self): 877 def SaveConfig(self):
863 gclient_utils.FileWrite(os.path.join(self.root_dir, 878 gclient_utils.FileWrite(os.path.join(self.root_dir,
864 self._options.config_filename), 879 self._options.config_filename),
(...skipping 716 matching lines...) Expand 10 before | Expand all | Expand 10 after
1581 except (gclient_utils.Error, subprocess2.CalledProcessError), e: 1596 except (gclient_utils.Error, subprocess2.CalledProcessError), e:
1582 print >> sys.stderr, 'Error: %s' % str(e) 1597 print >> sys.stderr, 'Error: %s' % str(e)
1583 return 1 1598 return 1
1584 1599
1585 1600
1586 if '__main__' == __name__: 1601 if '__main__' == __name__:
1587 fix_encoding.fix_encoding() 1602 fix_encoding.fix_encoding()
1588 sys.exit(Main(sys.argv[1:])) 1603 sys.exit(Main(sys.argv[1:]))
1589 1604
1590 # vim: ts=2:sw=2:tw=80:et: 1605 # vim: ts=2:sw=2:tw=80:et:
OLDNEW
« no previous file with comments | « no previous file | tests/gclient_test.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698