OLD | NEW |
1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
2 # Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. | 2 # Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. |
3 # | 3 # |
4 # Use of this source code is governed by a BSD-style license | 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 | 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 | 6 # tree. An additional intellectual property rights grant can be found |
7 # in the file PATENTS. All contributing project authors may | 7 # in the file PATENTS. All contributing project authors may |
8 # be found in the AUTHORS file in the root of the source tree. | 8 # be found in the AUTHORS file in the root of the source tree. |
9 | 9 |
10 """Setup links to a Chromium checkout for WebRTC. | 10 """Setup links to a Chromium checkout for WebRTC. |
11 | 11 |
12 WebRTC standalone shares a lot of dependencies and build tools with Chromium. | 12 WebRTC standalone shares a lot of dependencies and build tools with Chromium. |
13 To do this, many of the paths of a Chromium checkout is emulated by creating | 13 To do this, many of the paths of a Chromium checkout is emulated by creating |
14 symlinks to files and directories. This script handles the setup of symlinks to | 14 symlinks to files and directories. This script handles the setup of symlinks to |
15 achieve this. | 15 achieve this. |
16 | |
17 It also handles cleanup of the legacy Subversion-based approach that was used | |
18 before Chrome switched over their master repo from Subversion to Git. | |
19 """ | 16 """ |
20 | 17 |
21 | 18 |
22 import ctypes | 19 import ctypes |
23 import errno | 20 import errno |
24 import logging | 21 import logging |
25 import optparse | 22 import optparse |
26 import os | 23 import os |
27 import shelve | 24 import shelve |
28 import shutil | 25 import shutil |
(...skipping 212 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
241 | 238 |
242 os.symlink(source_path, os.path.abspath(self._link_path)) | 239 os.symlink(source_path, os.path.abspath(self._link_path)) |
243 links_db[self._source_path] = self._link_path | 240 links_db[self._source_path] = self._link_path |
244 | 241 |
245 | 242 |
246 class LinkError(IOError): | 243 class LinkError(IOError): |
247 """Failed to create a link.""" | 244 """Failed to create a link.""" |
248 pass | 245 pass |
249 | 246 |
250 | 247 |
251 # Handles symlink creation on the different platforms. | 248 # Use junctions instead of symlinks on the Windows platform. |
252 if sys.platform.startswith('win'): | 249 if sys.platform.startswith('win'): |
253 def symlink(source_path, link_path): | 250 def symlink(source_path, link_path): |
254 flag = 1 if os.path.isdir(source_path) else 0 | 251 if os.path.isdir(source_path): |
255 if not ctypes.windll.kernel32.CreateSymbolicLinkW( | 252 subprocess.check_call(['cmd.exe', '/c', 'mklink', '/J', link_path, |
256 unicode(link_path), unicode(source_path), flag): | 253 source_path]) |
257 raise OSError('Failed to create symlink to %s. Notice that only NTFS ' | 254 else: |
258 'version 5.0 and up has all the needed APIs for ' | 255 # Don't create symlinks to files on Windows, just copy the file instead |
259 'creating symlinks.' % source_path) | 256 # (there's no way to create a link without administrator's privileges). |
| 257 shutil.copy(source_path, link_path) |
260 os.symlink = symlink | 258 os.symlink = symlink |
261 | 259 |
262 | 260 |
263 class WebRTCLinkSetup(object): | 261 class WebRTCLinkSetup(object): |
264 def __init__(self, links_db, force=False, dry_run=False, prompt=False): | 262 def __init__(self, links_db, force=False, dry_run=False, prompt=False): |
265 self._force = force | 263 self._force = force |
266 self._dry_run = dry_run | 264 self._dry_run = dry_run |
267 self._prompt = prompt | 265 self._prompt = prompt |
268 self._links_db = links_db | 266 self._links_db = links_db |
269 | 267 |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
301 for action in (a for a in actions if a.dangerous): | 299 for action in (a for a in actions if a.dangerous): |
302 action.announce(planning=True) | 300 action.announce(planning=True) |
303 print | 301 print |
304 | 302 |
305 if not self._force: | 303 if not self._force: |
306 logging.error(textwrap.dedent("""\ | 304 logging.error(textwrap.dedent("""\ |
307 @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ | 305 @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ |
308 A C T I O N R E Q I R E D | 306 A C T I O N R E Q I R E D |
309 @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ | 307 @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ |
310 | 308 |
311 Because chromium/src is transitioning to Git (from SVN), we needed to | 309 Setting up the checkout requires creating symlinks to directories in the |
312 change the way that the WebRTC standalone checkout works. Instead of | 310 Chromium checkout inside chromium/src. |
313 individually syncing subdirectories of Chromium in SVN, we're now | 311 To avoid disrupting developers, we've chosen to not delete directories |
314 syncing Chromium (and all of its DEPS, as defined by its own DEPS file), | 312 forcibly, in case you have some work in progress in one of them :) |
315 into the `chromium/src` directory. | |
316 | |
317 As such, all Chromium directories which are currently pulled by DEPS are | |
318 now replaced with a symlink into the full Chromium checkout. | |
319 | |
320 To avoid disrupting developers, we've chosen to not delete your | |
321 directories forcibly, in case you have some work in progress in one of | |
322 them :). | |
323 | 313 |
324 ACTION REQUIRED: | 314 ACTION REQUIRED: |
325 Before running `gclient sync|runhooks` again, you must run: | 315 Before running `gclient sync|runhooks` again, you must run: |
326 %s%s --force | 316 %s%s --force |
327 | 317 |
328 Which will replace all directories which now must be symlinks, after | 318 Which will replace all directories which now must be symlinks, after |
329 prompting with a summary of the work-to-be-done. | 319 prompting with a summary of the work-to-be-done. |
330 """), 'python ' if sys.platform.startswith('win') else '', sys.argv[0]) | 320 """), 'python ' if sys.platform.startswith('win') else '', __file__) |
331 sys.exit(1) | 321 sys.exit(1) |
332 elif self._prompt: | 322 elif self._prompt: |
333 if not query_yes_no('Would you like to perform the above plan?'): | 323 if not query_yes_no('Would you like to perform the above plan?'): |
334 sys.exit(1) | 324 sys.exit(1) |
335 | 325 |
336 for action in actions: | 326 for action in actions: |
337 action.announce(planning=False) | 327 action.announce(planning=False) |
338 action.doit(self._links_db) | 328 action.doit(self._links_db) |
339 | 329 |
340 if not on_bot and self._force: | 330 if not on_bot and self._force: |
(...skipping 17 matching lines...) Expand all Loading... |
358 shell=True) | 348 shell=True) |
359 else: | 349 else: |
360 os.remove(link_path) | 350 os.remove(link_path) |
361 del self._links_db[source] | 351 del self._links_db[source] |
362 | 352 |
363 @staticmethod | 353 @staticmethod |
364 def _ActionForPath(source_path, link_path=None, check_fn=None, | 354 def _ActionForPath(source_path, link_path=None, check_fn=None, |
365 check_msg=None): | 355 check_msg=None): |
366 """Create zero or more Actions to link to a file or directory. | 356 """Create zero or more Actions to link to a file or directory. |
367 | 357 |
368 This will be a symlink on POSIX platforms. On Windows this requires | 358 This will be a symlink on POSIX platforms. On Windows it will result in: |
369 that NTFS is version 5.0 or higher (Vista or newer). | 359 * a junction for directories |
| 360 * a copied file for single files. |
370 | 361 |
371 Args: | 362 Args: |
372 source_path: Path relative to the Chromium checkout root. | 363 source_path: Path relative to the Chromium checkout root. |
373 For readability, the path may contain slashes, which will | 364 For readability, the path may contain slashes, which will |
374 automatically be converted to the right path delimiter on Windows. | 365 automatically be converted to the right path delimiter on Windows. |
375 link_path: The location for the link to create. If omitted it will be the | 366 link_path: The location for the link to create. If omitted it will be the |
376 same path as source_path. | 367 same path as source_path. |
377 check_fn: A function returning true if the type of filesystem object is | 368 check_fn: A function returning true if the type of filesystem object is |
378 correct for the attempted call. Otherwise an error message with | 369 correct for the attempted call. Otherwise an error message with |
379 check_msg will be printed. | 370 check_msg will be printed. |
380 check_msg: String used to inform the user of an invalid attempt to create | 371 check_msg: String used to inform the user of an invalid attempt to create |
381 a file. | 372 a file. |
382 Returns: | 373 Returns: |
383 A list of Action objects. | 374 A list of Action objects. |
384 """ | 375 """ |
385 def fix_separators(path): | 376 def fix_separators(path): |
386 if sys.platform.startswith('win'): | 377 if sys.platform.startswith('win'): |
387 return path.replace(os.altsep, os.sep) | 378 return path.replace(os.altsep, os.sep) |
388 else: | 379 else: |
389 return path | 380 return path |
390 | 381 |
391 assert check_fn | 382 assert check_fn |
392 assert check_msg | 383 assert check_msg |
393 link_path = link_path or source_path | 384 link_path = link_path or source_path |
394 link_path = fix_separators(link_path) | 385 link_path = fix_separators(link_path) |
395 | 386 |
396 source_path = fix_separators(source_path) | 387 source_path = fix_separators(source_path) |
397 source_path = os.path.join(CHROMIUM_CHECKOUT, source_path) | 388 source_path = os.path.join(CHROMIUM_CHECKOUT, source_path) |
398 if os.path.exists(source_path) and not check_fn: | 389 if os.path.exists(source_path) and not check_fn: |
399 raise LinkError('_LinkChromiumPath can only be used to link to %s: ' | 390 raise LinkError('Can only to link to %s: tried to link to: %s' % |
400 'Tried to link to: %s' % (check_msg, source_path)) | 391 (check_msg, source_path)) |
401 | 392 |
402 if not os.path.exists(source_path): | 393 if not os.path.exists(source_path): |
403 logging.debug('Silently ignoring missing source: %s. This is to avoid ' | 394 logging.debug('Silently ignoring missing source: %s. This is to avoid ' |
404 'errors on platform-specific dependencies.', source_path) | 395 'errors on platform-specific dependencies.', source_path) |
405 return [] | 396 return [] |
406 | 397 |
407 actions = [] | 398 actions = [] |
408 | 399 |
409 if os.path.exists(link_path) or os.path.islink(link_path): | 400 if os.path.exists(link_path) or os.path.islink(link_path): |
410 if os.path.islink(link_path): | 401 if os.path.islink(link_path): |
(...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
473 # Work from the root directory of the checkout. | 464 # Work from the root directory of the checkout. |
474 script_dir = os.path.dirname(os.path.abspath(__file__)) | 465 script_dir = os.path.dirname(os.path.abspath(__file__)) |
475 os.chdir(script_dir) | 466 os.chdir(script_dir) |
476 | 467 |
477 if sys.platform.startswith('win'): | 468 if sys.platform.startswith('win'): |
478 def is_admin(): | 469 def is_admin(): |
479 try: | 470 try: |
480 return os.getuid() == 0 | 471 return os.getuid() == 0 |
481 except AttributeError: | 472 except AttributeError: |
482 return ctypes.windll.shell32.IsUserAnAdmin() != 0 | 473 return ctypes.windll.shell32.IsUserAnAdmin() != 0 |
483 if not is_admin(): | 474 if is_admin(): |
484 logging.error('On Windows, you now need to have administrator ' | 475 logging.warning('WARNING: On Windows, you no longer need run as ' |
485 'privileges for the shell running %s (or ' | 476 'administrator. Please run with user account privileges.') |
486 '`gclient sync|runhooks`).\nPlease start another command ' | |
487 'prompt as Administrator and try again.', sys.argv[0]) | |
488 return 1 | |
489 | 477 |
490 if not os.path.exists(CHROMIUM_CHECKOUT): | 478 if not os.path.exists(CHROMIUM_CHECKOUT): |
491 logging.error('Cannot find a Chromium checkout at %s. Did you run "gclient ' | 479 logging.error('Cannot find a Chromium checkout at %s. Did you run "gclient ' |
492 'sync" before running this script?', CHROMIUM_CHECKOUT) | 480 'sync" before running this script?', CHROMIUM_CHECKOUT) |
493 return 2 | 481 return 2 |
494 | 482 |
495 links_database = _initialize_database(LINKS_DB) | 483 links_database = _initialize_database(LINKS_DB) |
496 try: | 484 try: |
497 symlink_creator = WebRTCLinkSetup(links_database, options.force, | 485 symlink_creator = WebRTCLinkSetup(links_database, options.force, |
498 options.dry_run, options.prompt) | 486 options.dry_run, options.prompt) |
499 symlink_creator.CleanupLinks() | 487 symlink_creator.CleanupLinks() |
500 if not options.clean_only: | 488 if not options.clean_only: |
501 symlink_creator.CreateLinks(on_bot) | 489 symlink_creator.CreateLinks(on_bot) |
502 except LinkError as e: | 490 except LinkError as e: |
503 print >> sys.stderr, e.message | 491 print >> sys.stderr, e.message |
504 return 3 | 492 return 3 |
505 finally: | 493 finally: |
506 links_database.close() | 494 links_database.close() |
507 return 0 | 495 return 0 |
508 | 496 |
509 | 497 |
510 if __name__ == '__main__': | 498 if __name__ == '__main__': |
511 sys.exit(main()) | 499 sys.exit(main()) |
OLD | NEW |