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

Side by Side Diff: setup_links.py

Issue 2359383002: setup_links.py: Use junctions instead of symlinks on Windows. (Closed)
Patch Set: Created 4 years, 3 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
« no previous file with comments | « no previous file | no next file » | 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) 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
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
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
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
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())
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698