OLD | NEW |
---|---|
1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
2 # Copyright 2014 The Chromium Authors. All rights reserved. | 2 # Copyright 2014 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 import codecs | 6 import codecs |
7 import copy | 7 import copy |
8 import cStringIO | |
9 import json | |
8 import optparse | 10 import optparse |
9 import os | 11 import os |
10 import pprint | 12 import pprint |
13 import re | |
11 import shutil | 14 import shutil |
12 import socket | 15 import socket |
13 import subprocess | 16 import subprocess |
14 import sys | 17 import sys |
15 import time | 18 import time |
19 import urllib2 | |
16 import urlparse | 20 import urlparse |
17 | 21 |
18 import os.path as path | 22 import os.path as path |
19 | 23 |
20 from common import chromium_utils | 24 from common import chromium_utils |
21 | 25 |
22 | 26 |
23 RECOGNIZED_PATHS = { | 27 RECOGNIZED_PATHS = { |
24 # If SVN path matches key, the entire URL is rewritten to the Git url. | 28 # If SVN path matches key, the entire URL is rewritten to the Git url. |
25 '/chrome/trunk/src': | 29 '/chrome/trunk/src': |
(...skipping 83 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
109 THIS_DIR = path.abspath(os.getcwd()) | 113 THIS_DIR = path.abspath(os.getcwd()) |
110 BUILDER_DIR = path.dirname(THIS_DIR) | 114 BUILDER_DIR = path.dirname(THIS_DIR) |
111 SLAVE_DIR = path.dirname(BUILDER_DIR) | 115 SLAVE_DIR = path.dirname(BUILDER_DIR) |
112 CACHE_DIR = path.join(SLAVE_DIR, 'cache_dir') | 116 CACHE_DIR = path.join(SLAVE_DIR, 'cache_dir') |
113 # Because we print CACHE_DIR out into a .gclient file, and then later run | 117 # Because we print CACHE_DIR out into a .gclient file, and then later run |
114 # eval() on it, backslashes need to be escaped, otherwise "E:\b\build" gets | 118 # eval() on it, backslashes need to be escaped, otherwise "E:\b\build" gets |
115 # parsed as "E:[\x08][\x08]uild". | 119 # parsed as "E:[\x08][\x08]uild". |
116 if sys.platform.startswith('win'): | 120 if sys.platform.startswith('win'): |
117 CACHE_DIR = CACHE_DIR.replace('\\', '\\\\') | 121 CACHE_DIR = CACHE_DIR.replace('\\', '\\\\') |
118 | 122 |
123 # Find the patch tool. | |
124 ROOT_BUILD_DIR = path.dirname(SLAVE_DIR) | |
125 ROOT_B_DIR = path.dirname(ROOT_BUILD_DIR) | |
126 BUILD_INTERNAL_DIR = path.join(ROOT_B_DIR, 'build_internal') | |
127 if sys.platform.startswith('win'): | |
128 PATCH_TOOL = path.join(BUILD_INTERNAL_DIR, 'tools', 'patch.EXE') | |
129 else: | |
130 PATCH_TOOL = '/usr/bin/patch' | |
131 | |
119 | 132 |
120 class SubprocessFailed(Exception): | 133 class SubprocessFailed(Exception): |
121 def __init__(self, message, code): | 134 def __init__(self, message, code): |
122 Exception.__init__(self, message) | 135 Exception.__init__(self, message) |
123 self.code = code | 136 self.code = code |
124 | 137 |
125 | 138 |
126 def call(*args, **kwargs): | 139 def call(*args, **kwargs): |
127 """Interactive subprocess call.""" | 140 """Interactive subprocess call.""" |
128 kwargs['stdout'] = subprocess.PIPE | 141 kwargs['stdout'] = subprocess.PIPE |
129 kwargs['stderr'] = subprocess.STDOUT | 142 kwargs['stderr'] = subprocess.STDOUT |
143 stdin_data = kwargs.pop('stdin_data', None) | |
144 if stdin_data: | |
145 kwargs['stdin'] = subprocess.PIPE | |
146 out = cStringIO.StringIO() | |
130 for attempt in xrange(RETRIES): | 147 for attempt in xrange(RETRIES): |
131 attempt_msg = ' (retry #%d)' % attempt if attempt else '' | 148 attempt_msg = ' (retry #%d)' % attempt if attempt else '' |
132 print '===Running %s%s===' % (' '.join(args), attempt_msg) | 149 print '===Running %s%s===' % (' '.join(args), attempt_msg) |
133 start_time = time.time() | 150 start_time = time.time() |
134 proc = subprocess.Popen(args, **kwargs) | 151 proc = subprocess.Popen(args, **kwargs) |
152 if stdin_data: | |
153 proc.stdin.write(stdin_data) | |
154 proc.stdin.close() | |
135 # This is here because passing 'sys.stdout' into stdout for proc will | 155 # This is here because passing 'sys.stdout' into stdout for proc will |
136 # produce out of order output. | 156 # produce out of order output. |
137 while True: | 157 while True: |
138 buf = proc.stdout.read(1) | 158 buf = proc.stdout.read(1) |
139 if not buf: | 159 if not buf: |
140 break | 160 break |
141 sys.stdout.write(buf) | 161 sys.stdout.write(buf) |
162 out.write(buf) | |
142 code = proc.wait() | 163 code = proc.wait() |
143 elapsed_time = ((time.time() - start_time) / 60.0) | 164 elapsed_time = ((time.time() - start_time) / 60.0) |
144 if not code: | 165 if not code: |
145 print '===Succeeded in %.1f mins===' % elapsed_time | 166 print '===Succeeded in %.1f mins===' % elapsed_time |
146 print | 167 print |
147 return 0 | 168 return out.getvalue() |
148 print '===Failed in %.1f mins===' % elapsed_time | 169 print '===Failed in %.1f mins===' % elapsed_time |
149 print | 170 print |
150 | 171 |
151 raise SubprocessFailed('%s failed with code %d in %s after %d attempts.' % | 172 raise SubprocessFailed('%s failed with code %d in %s after %d attempts.' % |
152 (' '.join(args), code, os.getcwd(), RETRIES), code) | 173 (' '.join(args), code, os.getcwd(), RETRIES), code) |
153 | 174 |
154 | 175 |
155 def git(*args, **kwargs): | 176 def git(*args, **kwargs): |
156 """Wrapper around call specifically for Git commands.""" | 177 """Wrapper around call specifically for Git commands.""" |
157 git_executable = 'git' | 178 git_executable = 'git' |
158 # On windows, subprocess doesn't fuzzy-match 'git' to 'git.bat', so we | 179 # On windows, subprocess doesn't fuzzy-match 'git' to 'git.bat', so we |
159 # have to do it explicitly. This is better than passing shell=True. | 180 # have to do it explicitly. This is better than passing shell=True. |
160 if sys.platform.startswith('win'): | 181 if sys.platform.startswith('win'): |
161 git_executable += '.bat' | 182 git_executable += '.bat' |
162 cmd = (git_executable,) + args | 183 cmd = (git_executable,) + args |
163 call(*cmd, **kwargs) | 184 return call(*cmd, **kwargs) |
164 | 185 |
165 | 186 |
166 def get_gclient_spec(solutions): | 187 def get_gclient_spec(solutions): |
167 return GCLIENT_TEMPLATE % { | 188 return GCLIENT_TEMPLATE % { |
168 'solutions': pprint.pformat(solutions, indent=4), | 189 'solutions': pprint.pformat(solutions, indent=4), |
169 'cache_dir': '"%s"' % CACHE_DIR | 190 'cache_dir': '"%s"' % CACHE_DIR |
170 } | 191 } |
171 | 192 |
172 | 193 |
173 def check_enabled(master, builder, slave): | 194 def check_enabled(master, builder, slave): |
(...skipping 103 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
277 with codecs.open('.gclient', mode='w', encoding='utf-8') as f: | 298 with codecs.open('.gclient', mode='w', encoding='utf-8') as f: |
278 f.write(get_gclient_spec(solutions)) | 299 f.write(get_gclient_spec(solutions)) |
279 | 300 |
280 | 301 |
281 def gclient_sync(): | 302 def gclient_sync(): |
282 gclient_bin = 'gclient.bat' if sys.platform.startswith('win') else 'gclient' | 303 gclient_bin = 'gclient.bat' if sys.platform.startswith('win') else 'gclient' |
283 call(gclient_bin, 'sync', '--verbose', '--reset', '--force', | 304 call(gclient_bin, 'sync', '--verbose', '--reset', '--force', |
284 '--nohooks', '--noprehooks') | 305 '--nohooks', '--noprehooks') |
285 | 306 |
286 | 307 |
308 def create_less_than_or_equal_regex(number): | |
agable
2014/02/20 00:13:06
Nooooooooooooo.
At the very least can we update t
Ryan Tseng
2014/02/20 01:04:37
Imagine if you set chrome's src repo to some pinne
| |
309 """ Return a regular expression to test whether an integer less than or equal | |
310 to 'number' is present in a given string. | |
311 """ | |
312 | |
313 # In three parts, build a regular expression that match any numbers smaller | |
314 # than 'number'. | |
315 # For example, 78656 would give a regular expression that looks like: | |
316 # Part 1 | |
317 # (78356| # 78356 | |
318 # Part 2 | |
319 # 7835[0-5]| # 78350-78355 | |
320 # 783[0-4][0-9]| # 78300-78349 | |
321 # 78[0-2][0-9][0-9]| # 78000-78299 | |
322 # 7[0-7][0-9][0-9][0-9]| # 70000-77999 | |
323 # [0-6][0-9][0-9][0-9][0-9]| # 10000-69999 | |
324 # Part 3 | |
325 # [0-9][0-9][0-9][0-9]| # 1000-9999 | |
326 # [0-9][0-9][0-9]| # 100-999 | |
327 # [0-9][0-9]| # 10-99 | |
328 # [0-9]) # 0-9 | |
329 | |
330 # Part 1: Create an array with all the regexes, as described above. | |
331 # Prepopulate it with the number itself. | |
332 number = str(number) | |
333 expressions = [number] | |
334 | |
335 # Convert the number to a list, so we can translate digits in it to | |
336 # expressions. | |
337 num_list = list(number) | |
338 num_len = len(num_list) | |
339 | |
340 # Part 2: Go through all the digits in the number, starting from the end. | |
341 # Each iteration appends a line to 'expressions'. | |
342 for index in range (num_len - 1, -1, -1): | |
343 # Convert this digit back to an integer. | |
344 digit = int(num_list[index]) | |
345 | |
346 # Part 2.1: No processing if this digit is a zero. | |
347 if digit == 0: | |
348 continue | |
349 | |
350 # Part 2.2: We switch the current digit X by a range "[0-(X-1)]". | |
351 num_list[index] = '[0-%d]' % (digit - 1) | |
352 | |
353 # Part 2.3: We set all following digits to be "[0-9]". | |
354 # Since we just decrementented a digit in a most important position, all | |
355 # following digits don't matter. The possible numbers will always be smaller | |
356 # than before we decremented. | |
357 for next_digit in range(index + 1, num_len): | |
358 num_list[next_digit] = '[0-9]' | |
359 | |
360 # Part 2.4: Add this new sub-expression to the list. | |
361 expressions.append(''.join(num_list)) | |
362 | |
363 # Part 3: We add all the full ranges to match all numbers that are at least | |
364 # one order of magnitude smaller than the original numbers. | |
365 for index in range(1, num_len): | |
366 expressions.append('[0-9]'*index) | |
367 | |
368 # All done. We now have our final regular expression. | |
369 regex = '(%s)' % ('|'.join(expressions)) | |
370 return regex | |
371 | |
372 | |
287 def get_git_hash(revision, dir_name): | 373 def get_git_hash(revision, dir_name): |
288 match = "^git-svn-id: [^ ]*@%d" % revision | 374 match = "^git-svn-id: [^ ]*@%s " % create_less_than_or_equal_regex(revision) |
289 cmd = ['git', 'log', '--grep', match, '--format=%H', dir_name] | 375 cmd = ['git', 'log', '-E', '--grep', match, '--format=%H', '--max-count=1'] |
290 return subprocess.check_output(cmd).strip() or None | 376 results = call(*cmd, cwd=dir_name).strip().splitlines() |
377 if results: | |
378 return results[0] | |
379 raise Exception('We can\'t resolve svn revision %s into a git hash' % | |
380 revision) | |
291 | 381 |
292 | 382 |
293 def deps2git(sln_dirs): | 383 def deps2git(sln_dirs): |
294 for sln_dir in sln_dirs: | 384 for sln_dir in sln_dirs: |
295 deps_file = path.join(os.getcwd(), sln_dir, 'DEPS') | 385 deps_file = path.join(os.getcwd(), sln_dir, 'DEPS') |
296 deps_git_file = path.join(os.getcwd(), sln_dir, '.DEPS.git') | 386 deps_git_file = path.join(os.getcwd(), sln_dir, '.DEPS.git') |
297 if not path.isfile(deps_file): | 387 if not path.isfile(deps_file): |
298 return | 388 return |
299 # Do we have a better way of doing this....? | 389 # Do we have a better way of doing this....? |
300 repo_type = 'internal' if 'internal' in sln_dir else 'public' | 390 repo_type = 'internal' if 'internal' in sln_dir else 'public' |
301 call(sys.executable, DEPS2GIT_PATH, '-t', repo_type, | 391 call(sys.executable, DEPS2GIT_PATH, '-t', repo_type, |
302 '--cache_dir=%s' % CACHE_DIR, | 392 '--cache_dir=%s' % CACHE_DIR, |
303 '--deps=%s' % deps_file, '--out=%s' % deps_git_file) | 393 '--deps=%s' % deps_file, '--out=%s' % deps_git_file) |
304 | 394 |
305 | 395 |
396 def emit_got_revision(revision): | |
397 print '@@@SET_BUILD_PROPERTY@got_revision@%s@@@' % revision | |
398 | |
306 def git_checkout(solutions, revision): | 399 def git_checkout(solutions, revision): |
307 build_dir = os.getcwd() | 400 build_dir = os.getcwd() |
308 # Revision only applies to the first solution. | 401 # Revision only applies to the first solution. |
309 first_solution = True | 402 first_solution = True |
310 for sln in solutions: | 403 for sln in solutions: |
311 name = sln['name'] | 404 name = sln['name'] |
312 url = sln['url'] | 405 url = sln['url'] |
313 sln_dir = path.join(build_dir, name) | 406 sln_dir = path.join(build_dir, name) |
314 if not path.isdir(sln_dir): | 407 if not path.isdir(sln_dir): |
315 git('clone', url, sln_dir) | 408 git('clone', url, sln_dir) |
316 | 409 |
317 # Clean out .DEPS.git changes first. | 410 # Clean out .DEPS.git changes first. |
318 try: | 411 try: |
319 git('reset', '--hard', cwd=sln_dir) | 412 git('reset', '--hard', cwd=sln_dir) |
320 except SubprocessFailed as e: | 413 except SubprocessFailed as e: |
321 if e.code == 128: | 414 if e.code == 128: |
322 # Exited abnormally, theres probably something wrong with the checkout. | 415 # Exited abnormally, theres probably something wrong with the checkout. |
323 # Lets wipe the checkout and try again. | 416 # Lets wipe the checkout and try again. |
324 chromium_utils.RemoveDirectory(sln_dir) | 417 chromium_utils.RemoveDirectory(sln_dir) |
325 git('clone', url, sln_dir) | 418 git('clone', url, sln_dir) |
326 git('reset', '--hard', cwd=sln_dir) | 419 git('reset', '--hard', cwd=sln_dir) |
327 else: | 420 else: |
328 raise | 421 raise |
329 | 422 |
330 git('clean', '-df', cwd=sln_dir) | 423 git('clean', '-df', cwd=sln_dir) |
331 git('pull', 'origin', 'master', cwd=sln_dir) | 424 git('pull', 'origin', 'master', cwd=sln_dir) |
332 # TODO(hinoka): We probably have to make use of revision mapping. | 425 # TODO(hinoka): We probably have to make use of revision mapping. |
333 if first_solution and revision and revision.lower() != 'head': | 426 if first_solution and revision and revision.lower() != 'head': |
427 emit_got_revision(revision) | |
334 if revision and revision.isdigit() and len(revision) < 40: | 428 if revision and revision.isdigit() and len(revision) < 40: |
335 # rev_num is really a svn revision number, convert it into a git hash. | 429 # rev_num is really a svn revision number, convert it into a git hash. |
336 git_ref = get_git_hash(revision, name) | 430 git_ref = get_git_hash(int(revision), name) |
337 else: | 431 else: |
338 # rev_num is actually a git hash or ref, we can just use it. | 432 # rev_num is actually a git hash or ref, we can just use it. |
339 git_ref = revision | 433 git_ref = revision |
340 git('checkout', git_ref, cwd=sln_dir) | 434 git('checkout', git_ref, cwd=sln_dir) |
341 else: | 435 else: |
342 git('checkout', 'origin/master', cwd=sln_dir) | 436 git('checkout', 'origin/master', cwd=sln_dir) |
437 if first_solution: | |
438 git_ref = git('log', '--format=%H', '--max-count=1', | |
439 cwd=sln_dir).strip() | |
343 | 440 |
344 first_solution = False | 441 first_solution = False |
442 return git_ref | |
345 | 443 |
346 | 444 |
347 def apply_issue(issue, patchset, root, server): | 445 def _download(url): |
348 pass | 446 """Fetch url and return content, with retries for flake.""" |
447 for attempt in xrange(RETRIES): | |
448 try: | |
449 return urllib2.urlopen(url).read() | |
450 except Exception: | |
451 if attempt == RETRIES - 1: | |
452 raise | |
453 pass | |
454 | |
455 | |
456 def apply_issue_svn(root, patch_url): | |
457 patch_data = call('svn', 'cat', patch_url) | |
458 call(PATCH_TOOL, '-p0', '--remove-empty-files', '--force', '--forward', | |
459 stdin_data=patch_data, cwd=root) | |
460 | |
461 | |
462 def apply_issue_rietveld(issue, patchset, root, server, rev_map, revision): | |
463 apply_issue_bin = ('apply_issue.bat' if sys.platform.startswith('win') | |
agable
2014/02/20 00:13:06
It's really truly necessary to be using apply_issu
Ryan Tseng
2014/02/20 01:04:37
apply_issue has a crapton of logic to deal with ev
| |
464 else 'apply_issue') | |
465 rev_map = json.loads(rev_map) | |
466 if root in rev_map and rev_map[root] == 'got_revision': | |
467 rev_map[root] = revision | |
468 call(apply_issue_bin, | |
469 '--root_dir', root, | |
470 '--issue', issue, | |
471 '--patchset', patchset, | |
472 '--no-auth', | |
473 '--server', server, | |
474 '--revision-mapping', json.dumps(rev_map), | |
475 '--base_ref', revision, | |
476 '--force') | |
349 | 477 |
350 | 478 |
351 def check_flag(flag_file): | 479 def check_flag(flag_file): |
352 """Returns True if the flag file is present.""" | 480 """Returns True if the flag file is present.""" |
353 return os.path.isfile(flag_file) | 481 return os.path.isfile(flag_file) |
354 | 482 |
355 | 483 |
356 def delete_flag(flag_file): | 484 def delete_flag(flag_file): |
357 """Remove bot update flag.""" | 485 """Remove bot update flag.""" |
358 if os.path.isfile(flag_file): | 486 if os.path.isfile(flag_file): |
359 os.remove(flag_file) | 487 os.remove(flag_file) |
360 | 488 |
361 | 489 |
362 def emit_flag(flag_file): | 490 def emit_flag(flag_file): |
363 """Deposit a bot update flag on the system to tell gclient not to run.""" | 491 """Deposit a bot update flag on the system to tell gclient not to run.""" |
364 print 'Emitting flag file at %s' % flag_file | 492 print 'Emitting flag file at %s' % flag_file |
365 with open(flag_file, 'wb') as f: | 493 with open(flag_file, 'wb') as f: |
366 f.write('Success!') | 494 f.write('Success!') |
367 | 495 |
368 | 496 |
369 def parse_args(): | 497 def parse_args(): |
370 parse = optparse.OptionParser() | 498 parse = optparse.OptionParser() |
371 | 499 |
372 parse.add_option('--issue', help='Issue number to patch from.') | 500 parse.add_option('--issue', help='Issue number to patch from.') |
373 parse.add_option('--patchset', | 501 parse.add_option('--patchset', |
374 help='Patchset from issue to patch from, if applicable.') | 502 help='Patchset from issue to patch from, if applicable.') |
375 parse.add_option('--patch_url', help='Optional URL to SVN patch.') | 503 parse.add_option('--patch_url', help='Optional URL to SVN patch.') |
376 parse.add_option('--root', help='Repository root.') | 504 parse.add_option('--root', help='Repository root.') |
377 parse.add_option('--rietveld_server', help='Rietveld server.') | 505 parse.add_option('--rietveld_server', |
506 default='codereview.chromium.org', | |
507 help='Rietveld server.') | |
378 parse.add_option('--specs', help='Gcilent spec.') | 508 parse.add_option('--specs', help='Gcilent spec.') |
379 parse.add_option('--master', help='Master name.') | 509 parse.add_option('--master', help='Master name.') |
380 parse.add_option('-f', '--force', action='store_true', | 510 parse.add_option('-f', '--force', action='store_true', |
381 help='Bypass check to see if we want to be run. ' | 511 help='Bypass check to see if we want to be run. ' |
382 'Should ONLY be used locally.') | 512 'Should ONLY be used locally.') |
383 # TODO(hinoka): We don't actually use this yet, we should factor this in. | 513 parse.add_option('--revision_mapping') |
384 parse.add_option('--revision-mapping') | |
385 parse.add_option('--revision') | 514 parse.add_option('--revision') |
386 parse.add_option('--slave_name', default=socket.getfqdn().split('.')[0], | 515 parse.add_option('--slave_name', default=socket.getfqdn().split('.')[0], |
387 help='Hostname of the current machine, ' | 516 help='Hostname of the current machine, ' |
388 'used for determining whether or not to activate.') | 517 'used for determining whether or not to activate.') |
389 parse.add_option('--builder_name', help='Name of the builder, ' | 518 parse.add_option('--builder_name', help='Name of the builder, ' |
390 'used for determining whether or not to activate.') | 519 'used for determining whether or not to activate.') |
391 parse.add_option('--build_dir', default=os.getcwd()) | 520 parse.add_option('--build_dir', default=os.getcwd()) |
392 parse.add_option('--flag_file', default=path.join(os.getcwd(), | 521 parse.add_option('--flag_file', default=path.join(os.getcwd(), |
393 'update.flag')) | 522 'update.flag')) |
394 | 523 |
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
430 ensure_no_checkout(dir_names, '.svn') | 559 ensure_no_checkout(dir_names, '.svn') |
431 emit_flag(options.flag_file) | 560 emit_flag(options.flag_file) |
432 else: | 561 else: |
433 delete_flag(options.flag_file) | 562 delete_flag(options.flag_file) |
434 return | 563 return |
435 | 564 |
436 # Get a checkout of each solution, without DEPS or hooks. | 565 # Get a checkout of each solution, without DEPS or hooks. |
437 # Calling git directory because there is no way to run Gclient without | 566 # Calling git directory because there is no way to run Gclient without |
438 # invoking DEPS. | 567 # invoking DEPS. |
439 print 'Fetching Git checkout' | 568 print 'Fetching Git checkout' |
440 git_checkout(git_solutions, options.revision) | 569 got_revision = git_checkout(git_solutions, options.revision) |
441 | 570 |
442 # TODO(hinoka): This must be implemented before we can turn this on for TS. | 571 options.root = options.root or dir_names[0] |
443 # if options.issue: | 572 if options.patch_url: |
444 # apply_issue(options.issue, options.patchset, options.root, options.server) | 573 apply_issue_svn(options.root, options.patch_url) |
574 elif options.issue: | |
575 apply_issue_rietveld(options.issue, options.patchset, options.root, | |
576 options.rietveld_server, options.revision_mapping, | |
577 got_revision) | |
445 | 578 |
446 # Magic to get deps2git to work with internal DEPS. | 579 # Magic to get deps2git to work with internal DEPS. |
447 shutil.copyfile(S2G_INTERNAL_FROM_PATH, S2G_INTERNAL_DEST_PATH) | 580 shutil.copyfile(S2G_INTERNAL_FROM_PATH, S2G_INTERNAL_DEST_PATH) |
448 deps2git(dir_names) | 581 deps2git(dir_names) |
449 | 582 |
450 gclient_configure(git_solutions) | 583 gclient_configure(git_solutions) |
451 gclient_sync() | 584 gclient_sync() |
452 | 585 |
453 | 586 |
454 if __name__ == '__main__': | 587 if __name__ == '__main__': |
455 sys.exit(main()) | 588 sys.exit(main()) |
OLD | NEW |