blob: 07f92ee85296ffa197288b7c1953b6177f642835 [file] [log] [blame]
kjellander@webrtc.org89256622014-08-20 12:10:11 +00001#!/usr/bin/env python
2# Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
3#
4# Use of this source code is governed by a BSD-style license
5# that can be found in the LICENSE file in the root of the source
6# tree. An additional intellectual property rights grant can be found
7# in the file PATENTS. All contributing project authors may
8# be found in the AUTHORS file in the root of the source tree.
9
10"""Setup links to a Chromium checkout for WebRTC.
11
12WebRTC standalone shares a lot of dependencies and build tools with Chromium.
13To do this, many of the paths of a Chromium checkout is emulated by creating
14symlinks to files and directories. This script handles the setup of symlinks to
15achieve this.
kjellander@webrtc.org89256622014-08-20 12:10:11 +000016"""
17
18
19import ctypes
20import errno
21import logging
22import optparse
23import os
24import shelve
25import shutil
26import subprocess
27import sys
28import textwrap
29
30
31DIRECTORIES = [
32 'build',
33 'buildtools',
kjellandere26e7872016-03-04 14:39:28 -080034 'mojo', # TODO(kjellander): Remove, see webrtc:5629.
kjellander@webrtc.org89256622014-08-20 12:10:11 +000035 'testing',
metzmanf89a5712016-07-25 02:14:09 -070036 'third_party/afl',
kjellander@webrtc.org89256622014-08-20 12:10:11 +000037 'third_party/binutils',
38 'third_party/boringssl',
kjellander24d812d2016-11-22 07:02:11 -080039 'third_party/catapult',
ehmaldonado1249ddf2016-08-31 01:58:48 -070040 'third_party/closure_compiler',
kjellander@webrtc.org89256622014-08-20 12:10:11 +000041 'third_party/colorama',
kjellander@webrtc.org89256622014-08-20 12:10:11 +000042 'third_party/expat',
hbosa9a1d2a2016-01-11 10:19:02 -080043 'third_party/ffmpeg',
kjellander@webrtc.org4e4fe4f2014-10-01 08:03:19 +000044 'third_party/instrumented_libraries',
kjellander@webrtc.org89256622014-08-20 12:10:11 +000045 'third_party/jsoncpp',
Henrik Kjellander26ab91b2015-11-26 10:26:32 +010046 'third_party/libc++-static',
kjellander@webrtc.org2addf3f2016-04-04 08:33:12 +020047 'third_party/libFuzzer',
kjellander@webrtc.org89256622014-08-20 12:10:11 +000048 'third_party/libjpeg',
49 'third_party/libjpeg_turbo',
50 'third_party/libsrtp',
kjellandere26e7872016-03-04 14:39:28 -080051 'third_party/libvpx',
kjellander@webrtc.org89256622014-08-20 12:10:11 +000052 'third_party/libyuv',
53 'third_party/llvm-build',
Patrik Höglundc92c23d2015-08-31 11:30:14 +020054 'third_party/lss',
kjellander@webrtc.org89256622014-08-20 12:10:11 +000055 'third_party/nss',
tkchin@webrtc.org3a63a3c2015-01-06 07:21:34 +000056 'third_party/ocmock',
hbosa9a1d2a2016-01-11 10:19:02 -080057 'third_party/openh264',
kjellander@webrtc.org89256622014-08-20 12:10:11 +000058 'third_party/openmax_dl',
59 'third_party/opus',
Patrik Höglundc92c23d2015-08-31 11:30:14 +020060 'third_party/proguard',
kjellander@webrtc.org89256622014-08-20 12:10:11 +000061 'third_party/protobuf',
62 'third_party/sqlite',
63 'third_party/syzygy',
64 'third_party/usrsctp',
65 'third_party/yasm',
kjellander@webrtc.org3bd41562014-09-01 11:06:37 +000066 'third_party/zlib',
kjellander752f36f2016-03-22 16:56:01 -070067 'third_party/WebKit', # TODO(kjellander): Remove, see webrtc:5629.
kjellander@webrtc.org89256622014-08-20 12:10:11 +000068 'tools/clang',
kjellander24d812d2016-11-22 07:02:11 -080069 'tools/clang_format_merge_driver',
ehmaldonadoa78213e2016-09-22 10:46:16 -070070 'tools/determinism',
kjellander@webrtc.org89256622014-08-20 12:10:11 +000071 'tools/generate_library_loader',
hbos5602f652016-01-15 01:38:34 -080072 'tools/generate_stubs',
kjellander@webrtc.org89256622014-08-20 12:10:11 +000073 'tools/gn',
ehmaldonado1249ddf2016-08-31 01:58:48 -070074 'tools/grit',
kjellander@webrtc.org89256622014-08-20 12:10:11 +000075 'tools/gyp',
kjellandere532aec2016-04-17 20:08:20 -070076 'tools/luci-go',
kjellander@webrtc.org89256622014-08-20 12:10:11 +000077 'tools/memory',
78 'tools/protoc_wrapper',
79 'tools/python',
80 'tools/swarming_client',
81 'tools/valgrind',
Andrew MacDonald65de7d22015-05-19 11:37:34 -070082 'tools/vim',
kjellander@webrtc.org89256622014-08-20 12:10:11 +000083 'tools/win',
Henrik Kjellander9589e2a2015-10-22 06:48:21 +020084 'tools/xdisplaycheck',
kjellander@webrtc.org89256622014-08-20 12:10:11 +000085]
86
kjellander@webrtc.org3bd41562014-09-01 11:06:37 +000087from sync_chromium import get_target_os_list
Henrik Kjellanderca843022015-06-09 10:51:22 +020088target_os = get_target_os_list()
89if 'android' in target_os:
kjellander@webrtc.org3bd41562014-09-01 11:06:37 +000090 DIRECTORIES += [
91 'base',
ehmaldonado9f73f7a2016-07-28 01:20:22 -070092 'third_party/accessibility_test_framework',
Henrik Kjellander94a12322015-06-09 14:56:20 +020093 'third_party/android_platform',
kjellanderb1125682016-10-26 13:38:03 -070094 'third_party/android_support_test_runner',
kjellander@webrtc.org3bd41562014-09-01 11:06:37 +000095 'third_party/android_tools',
ehmaldonado9f73f7a2016-07-28 01:20:22 -070096 'third_party/apache_velocity',
kjellander@webrtc.orgcbe7ca82015-01-06 07:24:27 +000097 'third_party/appurify-python',
kjellander@webrtc.orgb8caf6a2014-09-30 18:05:02 +000098 'third_party/ashmem',
ehmaldonado9f73f7a2016-07-28 01:20:22 -070099 'third_party/bouncycastle',
ehmaldonadocd8ae612016-08-24 02:44:14 -0700100 'third_party/byte_buddy',
kjellanderf10976e2016-08-11 10:31:40 -0700101 'third_party/ced',
kjellander24d812d2016-11-22 07:02:11 -0800102 'third_party/espresso',
ehmaldonado9f73f7a2016-07-28 01:20:22 -0700103 'third_party/guava',
104 'third_party/hamcrest',
kjellander53761002015-11-10 10:58:22 -0800105 'third_party/icu',
ehmaldonado9f73f7a2016-07-28 01:20:22 -0700106 'third_party/icu4j',
Henrik Kjellandereecbab72015-09-16 19:19:04 +0200107 'third_party/ijar',
ehmaldonado9f73f7a2016-07-28 01:20:22 -0700108 'third_party/intellij',
kjellander24d812d2016-11-22 07:02:11 -0800109 'third_party/javax_inject',
kjellander@webrtc.orgb8caf6a2014-09-30 18:05:02 +0000110 'third_party/jsr-305',
Henrik Kjellander10ba3ee2015-04-29 14:47:53 +0200111 'third_party/junit',
kjellander@webrtc.orgb8caf6a2014-09-30 18:05:02 +0000112 'third_party/libxml',
Henrik Kjellander10ba3ee2015-04-29 14:47:53 +0200113 'third_party/mockito',
kjellander@webrtc.orgb8caf6a2014-09-30 18:05:02 +0000114 'third_party/modp_b64',
ehmaldonadocd8ae612016-08-24 02:44:14 -0700115 'third_party/objenesis',
ehmaldonado9f73f7a2016-07-28 01:20:22 -0700116 'third_party/ow2_asm',
kjellander@webrtc.orgcbe7ca82015-01-06 07:24:27 +0000117 'third_party/requests',
Henrik Kjellander10ba3ee2015-04-29 14:47:53 +0200118 'third_party/robolectric',
ehmaldonado9f73f7a2016-07-28 01:20:22 -0700119 'third_party/sqlite4java',
primianob332e5d2016-01-25 08:13:05 -0800120 'third_party/tcmalloc',
kjellander@webrtc.org3bd41562014-09-01 11:06:37 +0000121 'tools/android',
kjellander34a70542015-12-06 10:32:34 -0800122 'tools/telemetry',
kjellander@webrtc.org3bd41562014-09-01 11:06:37 +0000123 ]
tommide3b0292016-04-22 01:47:02 -0700124else:
125 DIRECTORIES += [
126 'base/third_party/libevent',
127 ]
128
Henrik Kjellanderca843022015-06-09 10:51:22 +0200129if 'ios' in target_os:
130 DIRECTORIES.append('third_party/class-dump')
kjellander@webrtc.org3bd41562014-09-01 11:06:37 +0000131
kjellander@webrtc.org89256622014-08-20 12:10:11 +0000132FILES = {
Henrik Kjellanderd6d27e72015-09-25 22:19:11 +0200133 'tools/isolate_driver.py': None,
kjellander@webrtc.org89256622014-08-20 12:10:11 +0000134 'third_party/BUILD.gn': None,
kjellander@webrtc.org89256622014-08-20 12:10:11 +0000135}
136
kjellander@webrtc.orge94f83a2014-09-18 13:47:23 +0000137ROOT_DIR = os.path.dirname(os.path.abspath(__file__))
kjellander@webrtc.org89256622014-08-20 12:10:11 +0000138CHROMIUM_CHECKOUT = os.path.join('chromium', 'src')
139LINKS_DB = 'links'
140
141# Version management to make future upgrades/downgrades easier to support.
142SCHEMA_VERSION = 1
143
144
145def query_yes_no(question, default=False):
146 """Ask a yes/no question via raw_input() and return their answer.
147
148 Modified from http://stackoverflow.com/a/3041990.
149 """
150 prompt = " [%s/%%s]: "
151 prompt = prompt % ('Y' if default is True else 'y')
152 prompt = prompt % ('N' if default is False else 'n')
153
154 if default is None:
155 default = 'INVALID'
156
157 while True:
158 sys.stdout.write(question + prompt)
159 choice = raw_input().lower()
160 if choice == '' and default != 'INVALID':
161 return default
162
163 if 'yes'.startswith(choice):
164 return True
165 elif 'no'.startswith(choice):
166 return False
167
168 print "Please respond with 'yes' or 'no' (or 'y' or 'n')."
169
170
171# Actions
172class Action(object):
173 def __init__(self, dangerous):
174 self.dangerous = dangerous
175
176 def announce(self, planning):
177 """Log a description of this action.
178
179 Args:
180 planning - True iff we're in the planning stage, False if we're in the
181 doit stage.
182 """
183 pass
184
185 def doit(self, links_db):
186 """Execute the action, recording what we did to links_db, if necessary."""
187 pass
188
189
190class Remove(Action):
191 def __init__(self, path, dangerous):
192 super(Remove, self).__init__(dangerous)
193 self._priority = 0
194 self._path = path
195
196 def announce(self, planning):
197 log = logging.warn
198 filesystem_type = 'file'
199 if not self.dangerous:
200 log = logging.info
201 filesystem_type = 'link'
202 if planning:
203 log('Planning to remove %s: %s', filesystem_type, self._path)
204 else:
205 log('Removing %s: %s', filesystem_type, self._path)
206
Henrik Kjellander57e5fd22015-05-25 12:55:39 +0200207 def doit(self, _):
kjellander@webrtc.org89256622014-08-20 12:10:11 +0000208 os.remove(self._path)
209
210
211class Rmtree(Action):
212 def __init__(self, path):
213 super(Rmtree, self).__init__(dangerous=True)
214 self._priority = 0
215 self._path = path
216
217 def announce(self, planning):
218 if planning:
219 logging.warn('Planning to remove directory: %s', self._path)
220 else:
221 logging.warn('Removing directory: %s', self._path)
222
Henrik Kjellander57e5fd22015-05-25 12:55:39 +0200223 def doit(self, _):
kjellander@webrtc.org89256622014-08-20 12:10:11 +0000224 if sys.platform.startswith('win'):
225 # shutil.rmtree() doesn't work on Windows if any of the directories are
226 # read-only, which svn repositories are.
227 subprocess.check_call(['rd', '/q', '/s', self._path], shell=True)
228 else:
229 shutil.rmtree(self._path)
230
231
232class Makedirs(Action):
233 def __init__(self, path):
234 super(Makedirs, self).__init__(dangerous=False)
235 self._priority = 1
236 self._path = path
237
Henrik Kjellander57e5fd22015-05-25 12:55:39 +0200238 def doit(self, _):
kjellander@webrtc.org89256622014-08-20 12:10:11 +0000239 try:
240 os.makedirs(self._path)
241 except OSError as e:
242 if e.errno != errno.EEXIST:
243 raise
244
245
246class Symlink(Action):
247 def __init__(self, source_path, link_path):
248 super(Symlink, self).__init__(dangerous=False)
249 self._priority = 2
250 self._source_path = source_path
251 self._link_path = link_path
252
253 def announce(self, planning):
254 if planning:
255 logging.info(
256 'Planning to create link from %s to %s', self._link_path,
257 self._source_path)
258 else:
259 logging.debug(
260 'Linking from %s to %s', self._link_path, self._source_path)
261
262 def doit(self, links_db):
263 # Files not in the root directory need relative path calculation.
264 # On Windows, use absolute paths instead since NTFS doesn't seem to support
265 # relative paths for symlinks.
266 if sys.platform.startswith('win'):
267 source_path = os.path.abspath(self._source_path)
268 else:
269 if os.path.dirname(self._link_path) != self._link_path:
270 source_path = os.path.relpath(self._source_path,
271 os.path.dirname(self._link_path))
272
273 os.symlink(source_path, os.path.abspath(self._link_path))
274 links_db[self._source_path] = self._link_path
275
276
277class LinkError(IOError):
278 """Failed to create a link."""
279 pass
280
281
kjellander844dd2a2016-04-05 00:13:58 -0700282# Use junctions instead of symlinks on the Windows platform.
kjellander@webrtc.org89256622014-08-20 12:10:11 +0000283if sys.platform.startswith('win'):
284 def symlink(source_path, link_path):
kjellander844dd2a2016-04-05 00:13:58 -0700285 if os.path.isdir(source_path):
286 subprocess.check_call(['cmd.exe', '/c', 'mklink', '/J', link_path,
287 source_path])
288 else:
289 # Don't create symlinks to files on Windows, just copy the file instead
290 # (there's no way to create a link without administrator's privileges).
291 shutil.copy(source_path, link_path)
kjellander@webrtc.org89256622014-08-20 12:10:11 +0000292 os.symlink = symlink
293
294
Henrik Kjellander57e5fd22015-05-25 12:55:39 +0200295class WebRTCLinkSetup(object):
kjellander@webrtc.org89256622014-08-20 12:10:11 +0000296 def __init__(self, links_db, force=False, dry_run=False, prompt=False):
297 self._force = force
298 self._dry_run = dry_run
299 self._prompt = prompt
300 self._links_db = links_db
301
302 def CreateLinks(self, on_bot):
303 logging.debug('CreateLinks')
304 # First, make a plan of action
305 actions = []
306
307 for source_path, link_path in FILES.iteritems():
308 actions += self._ActionForPath(
309 source_path, link_path, check_fn=os.path.isfile, check_msg='files')
310 for source_dir in DIRECTORIES:
311 actions += self._ActionForPath(
312 source_dir, None, check_fn=os.path.isdir,
313 check_msg='directories')
314
kjellander@webrtc.orge94f83a2014-09-18 13:47:23 +0000315 if not on_bot and self._force:
316 # When making the manual switch from legacy SVN checkouts to the new
317 # Git-based Chromium DEPS, the .gclient_entries file that contains cached
318 # URLs for all DEPS entries must be removed to avoid future sync problems.
319 entries_file = os.path.join(os.path.dirname(ROOT_DIR), '.gclient_entries')
320 if os.path.exists(entries_file):
321 actions.append(Remove(entries_file, dangerous=True))
322
kjellander@webrtc.org89256622014-08-20 12:10:11 +0000323 actions.sort()
324
325 if self._dry_run:
326 for action in actions:
327 action.announce(planning=True)
328 logging.info('Not doing anything because dry-run was specified.')
329 sys.exit(0)
330
331 if any(a.dangerous for a in actions):
332 logging.warn('Dangerous actions:')
333 for action in (a for a in actions if a.dangerous):
334 action.announce(planning=True)
335 print
336
337 if not self._force:
338 logging.error(textwrap.dedent("""\
339 @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
340 A C T I O N R E Q I R E D
341 @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
342
kjellander844dd2a2016-04-05 00:13:58 -0700343 Setting up the checkout requires creating symlinks to directories in the
344 Chromium checkout inside chromium/src.
345 To avoid disrupting developers, we've chosen to not delete directories
346 forcibly, in case you have some work in progress in one of them :)
kjellander@webrtc.org89256622014-08-20 12:10:11 +0000347
348 ACTION REQUIRED:
349 Before running `gclient sync|runhooks` again, you must run:
350 %s%s --force
351
352 Which will replace all directories which now must be symlinks, after
353 prompting with a summary of the work-to-be-done.
kjellander844dd2a2016-04-05 00:13:58 -0700354 """), 'python ' if sys.platform.startswith('win') else '', __file__)
kjellander@webrtc.org89256622014-08-20 12:10:11 +0000355 sys.exit(1)
356 elif self._prompt:
357 if not query_yes_no('Would you like to perform the above plan?'):
358 sys.exit(1)
359
360 for action in actions:
361 action.announce(planning=False)
362 action.doit(self._links_db)
363
364 if not on_bot and self._force:
365 logging.info('Completed!\n\nNow run `gclient sync|runhooks` again to '
366 'let the remaining hooks (that probably were interrupted) '
367 'execute.')
368
369 def CleanupLinks(self):
370 logging.debug('CleanupLinks')
371 for source, link_path in self._links_db.iteritems():
372 if source == 'SCHEMA_VERSION':
373 continue
374 if os.path.islink(link_path) or sys.platform.startswith('win'):
375 # os.path.islink() always returns false on Windows
376 # See http://bugs.python.org/issue13143.
377 logging.debug('Removing link to %s at %s', source, link_path)
378 if not self._dry_run:
379 if os.path.exists(link_path):
380 if sys.platform.startswith('win') and os.path.isdir(link_path):
Henrik Kjellanderc444de62015-04-29 11:27:22 +0200381 subprocess.check_call(['rmdir', '/q', '/s', link_path],
382 shell=True)
kjellander@webrtc.org89256622014-08-20 12:10:11 +0000383 else:
384 os.remove(link_path)
385 del self._links_db[source]
386
387 @staticmethod
388 def _ActionForPath(source_path, link_path=None, check_fn=None,
389 check_msg=None):
390 """Create zero or more Actions to link to a file or directory.
391
kjellander844dd2a2016-04-05 00:13:58 -0700392 This will be a symlink on POSIX platforms. On Windows it will result in:
393 * a junction for directories
394 * a copied file for single files.
kjellander@webrtc.org89256622014-08-20 12:10:11 +0000395
396 Args:
397 source_path: Path relative to the Chromium checkout root.
398 For readability, the path may contain slashes, which will
399 automatically be converted to the right path delimiter on Windows.
400 link_path: The location for the link to create. If omitted it will be the
401 same path as source_path.
402 check_fn: A function returning true if the type of filesystem object is
403 correct for the attempted call. Otherwise an error message with
404 check_msg will be printed.
405 check_msg: String used to inform the user of an invalid attempt to create
406 a file.
407 Returns:
408 A list of Action objects.
409 """
410 def fix_separators(path):
411 if sys.platform.startswith('win'):
412 return path.replace(os.altsep, os.sep)
413 else:
414 return path
415
416 assert check_fn
417 assert check_msg
418 link_path = link_path or source_path
419 link_path = fix_separators(link_path)
420
421 source_path = fix_separators(source_path)
422 source_path = os.path.join(CHROMIUM_CHECKOUT, source_path)
423 if os.path.exists(source_path) and not check_fn:
kjellander844dd2a2016-04-05 00:13:58 -0700424 raise LinkError('Can only to link to %s: tried to link to: %s' %
425 (check_msg, source_path))
kjellander@webrtc.org89256622014-08-20 12:10:11 +0000426
427 if not os.path.exists(source_path):
428 logging.debug('Silently ignoring missing source: %s. This is to avoid '
429 'errors on platform-specific dependencies.', source_path)
430 return []
431
432 actions = []
433
434 if os.path.exists(link_path) or os.path.islink(link_path):
435 if os.path.islink(link_path):
436 actions.append(Remove(link_path, dangerous=False))
437 elif os.path.isfile(link_path):
438 actions.append(Remove(link_path, dangerous=True))
439 elif os.path.isdir(link_path):
440 actions.append(Rmtree(link_path))
441 else:
442 raise LinkError('Don\'t know how to plan: %s' % link_path)
443
444 # Create parent directories to the target link if needed.
445 target_parent_dirs = os.path.dirname(link_path)
446 if (target_parent_dirs and
447 target_parent_dirs != link_path and
448 not os.path.exists(target_parent_dirs)):
449 actions.append(Makedirs(target_parent_dirs))
450
451 actions.append(Symlink(source_path, link_path))
452
453 return actions
454
455def _initialize_database(filename):
456 links_database = shelve.open(filename)
457
458 # Wipe the database if this version of the script ends up looking at a
459 # newer (future) version of the links db, just to be sure.
460 version = links_database.get('SCHEMA_VERSION')
461 if version and version != SCHEMA_VERSION:
462 logging.info('Found database with schema version %s while this script only '
463 'supports %s. Wiping previous database contents.', version,
464 SCHEMA_VERSION)
465 links_database.clear()
466 links_database['SCHEMA_VERSION'] = SCHEMA_VERSION
467 return links_database
468
469
470def main():
471 on_bot = os.environ.get('CHROME_HEADLESS') == '1'
472
473 parser = optparse.OptionParser()
474 parser.add_option('-d', '--dry-run', action='store_true', default=False,
475 help='Print what would be done, but don\'t perform any '
476 'operations. This will automatically set logging to '
477 'verbose.')
478 parser.add_option('-c', '--clean-only', action='store_true', default=False,
479 help='Only clean previously created links, don\'t create '
480 'new ones. This will automatically set logging to '
481 'verbose.')
482 parser.add_option('-f', '--force', action='store_true', default=on_bot,
483 help='Force link creation. CAUTION: This deletes existing '
484 'folders and files in the locations where links are '
485 'about to be created.')
486 parser.add_option('-n', '--no-prompt', action='store_false', dest='prompt',
487 default=(not on_bot),
488 help='Prompt if we\'re planning to do a dangerous action')
489 parser.add_option('-v', '--verbose', action='store_const',
490 const=logging.DEBUG, default=logging.INFO,
491 help='Print verbose output for debugging.')
492 options, _ = parser.parse_args()
493
494 if options.dry_run or options.force or options.clean_only:
495 options.verbose = logging.DEBUG
496 logging.basicConfig(format='%(message)s', level=options.verbose)
497
498 # Work from the root directory of the checkout.
499 script_dir = os.path.dirname(os.path.abspath(__file__))
500 os.chdir(script_dir)
501
502 if sys.platform.startswith('win'):
503 def is_admin():
504 try:
505 return os.getuid() == 0
506 except AttributeError:
507 return ctypes.windll.shell32.IsUserAnAdmin() != 0
kjellander844dd2a2016-04-05 00:13:58 -0700508 if is_admin():
509 logging.warning('WARNING: On Windows, you no longer need run as '
510 'administrator. Please run with user account privileges.')
kjellander@webrtc.org89256622014-08-20 12:10:11 +0000511
512 if not os.path.exists(CHROMIUM_CHECKOUT):
Henrik Kjellanderedec0762016-11-15 22:39:13 +0100513 logging.warning('Cannot find a Chromium checkout at %s. Did you run '
514 '"gclient sync" before running this script?',
515 CHROMIUM_CHECKOUT)
516 return 0
kjellander@webrtc.org89256622014-08-20 12:10:11 +0000517
518 links_database = _initialize_database(LINKS_DB)
519 try:
520 symlink_creator = WebRTCLinkSetup(links_database, options.force,
521 options.dry_run, options.prompt)
522 symlink_creator.CleanupLinks()
523 if not options.clean_only:
524 symlink_creator.CreateLinks(on_bot)
525 except LinkError as e:
526 print >> sys.stderr, e.message
527 return 3
528 finally:
529 links_database.close()
530 return 0
531
532
533if __name__ == '__main__':
534 sys.exit(main())