Per default Windows requires admin access to create symlinks, while junctions (which behave similarly) can be created by users. This function replaces symlinks (which might be broken when checked out without admin rights) with junctions so Ray can be built both with and without
()
| 477 | |
| 478 | |
| 479 | def replace_symlinks_with_junctions(): |
| 480 | """ |
| 481 | Per default Windows requires admin access to create symlinks, while |
| 482 | junctions (which behave similarly) can be created by users. |
| 483 | |
| 484 | This function replaces symlinks (which might be broken when checked |
| 485 | out without admin rights) with junctions so Ray can be built both |
| 486 | with and without admin access. |
| 487 | """ |
| 488 | assert is_native_windows_or_msys() |
| 489 | |
| 490 | # Update this list if new symlinks are introduced to the source tree |
| 491 | _LINKS = { |
| 492 | r"ray\rllib": "../../rllib", |
| 493 | } |
| 494 | root_dir = os.path.dirname(__file__) |
| 495 | for link, default in _LINKS.items(): |
| 496 | path = os.path.join(root_dir, link) |
| 497 | try: |
| 498 | out = subprocess.check_output( |
| 499 | "DIR /A:LD /B", shell=True, cwd=os.path.dirname(path) |
| 500 | ) |
| 501 | except subprocess.CalledProcessError: |
| 502 | out = b"" |
| 503 | if os.path.basename(path) in out.decode("utf8").splitlines(): |
| 504 | logger.info(f"'{link}' is already converted to junction point") |
| 505 | else: |
| 506 | logger.info(f"Converting '{link}' to junction point...") |
| 507 | if os.path.isfile(path): |
| 508 | with open(path) as inp: |
| 509 | target = inp.read() |
| 510 | os.unlink(path) |
| 511 | elif os.path.isdir(path): |
| 512 | target = default |
| 513 | try: |
| 514 | # unlink() works on links as well as on regular files, |
| 515 | # and links to directories are considered directories now |
| 516 | os.unlink(path) |
| 517 | except OSError as err: |
| 518 | # On Windows attempt to unlink a regular directory results |
| 519 | # in a PermissionError with errno set to errno.EACCES. |
| 520 | if err.errno != errno.EACCES: |
| 521 | raise |
| 522 | # For regular directories deletion is done with rmdir call. |
| 523 | os.rmdir(path) |
| 524 | else: |
| 525 | raise ValueError(f"Unexpected type of entry: '{path}'") |
| 526 | target = os.path.abspath(os.path.join(os.path.dirname(path), target)) |
| 527 | logger.info("Setting {} -> {}".format(link, target)) |
| 528 | subprocess.check_call( |
| 529 | f'MKLINK /J "{os.path.basename(link)}" "{target}"', |
| 530 | shell=True, |
| 531 | cwd=os.path.dirname(path), |
| 532 | ) |
| 533 | |
| 534 | |
| 535 | if is_conda_forge_build and is_native_windows_or_msys(): |
no test coverage detected
searching dependent graphs…