| 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 |