Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rewrite symlinks for vendored repositories #22723

Closed
wants to merge 16 commits into from

Conversation

meteorcloudy
Copy link
Member

@meteorcloudy meteorcloudy commented Jun 12, 2024

To make sure symlinks work correctly, Bazel uses the following strategy to rewrite symlinks in the vendored source:

  • Create a symlink <vendor_dir>/bazel-external that points to $(bazel info output_base)/external. It is refreshed by every Bazel command automatically.
  • For the vendored source, rewrite all symlinks that originally point to a path under $(bazel info output_base)/external to a relative path under <vendor_dir>/bazel-external.

Fixes #22303

@meteorcloudy meteorcloudy requested review from fmeum and removed request for gregestren and fweikert June 12, 2024 13:59
@github-actions github-actions bot added team-ExternalDeps External dependency handling, remote repositiories, WORKSPACE file. team-Documentation Documentation improvements that cannot be directly linked to other team labels awaiting-review PR is awaiting review from an assigned reviewer labels Jun 12, 2024
@meteorcloudy
Copy link
Member Author

Sign... TIL: Windows resolves symlinks differently. Somehow cat /c/tmp/... works, but cat C:/tmp/ doesn't.

pcloudy@bazel-windows-playground:/c/tmp/74bsjlkt/execroot/_main/_tmp/9293b221820db76a6f9697c2a01fde7c/tests_root/tmpdbw1krzm (rewrite_symlinks)
$ ls -l /c/tmp/74bsjlkt/execroot/_main/_tmp/9293b221820db76a6f9697c2a01fde7c/tests_root/tmpxyvsr7o6/external/_main~ext~foo/path_foo
lrwxrwxrwx 1 pcloudy None 37 Jun 12 19:08 /c/tmp/74bsjlkt/execroot/_main/_tmp/9293b221820db76a6f9697c2a01fde7c/tests_root/tmpxyvsr7o6/external/_main~ext~foo/path_fo
o -> ../_bazel-external/_main~ext~foo/data
pcloudy@bazel-windows-playground:/c/tmp/74bsjlkt/execroot/_main/_tmp/9293b221820db76a6f9697c2a01fde7c/tests_root/tmpdbw1krzm (rewrite_symlinks)
$ cat /c/tmp/74bsjlkt/execroot/_main/_tmp/9293b221820db76a6f9697c2a01fde7c/tests_root/tmpxyvsr7o6/external/_main~ext~foo/path_foo
Hello from foo!
pcloudy@bazel-windows-playground:/c/tmp/74bsjlkt/execroot/_main/_tmp/9293b221820db76a6f9697c2a01fde7c/tests_root/tmpdbw1krzm (rewrite_symlinks)
$ cat C:/tmp/74bsjlkt/execroot/_main/_tmp/9293b221820db76a6f9697c2a01fde7c/tests_root/tmpxyvsr7o6/external/_main~ext~foo/path_foo
cat: 'C:/tmp/74bsjlkt/execroot/_main/_tmp/9293b221820db76a6f9697c2a01fde7c/tests_root/tmpxyvsr7o6/external/_main~ext~foo/path_foo': No such file or directory

@meteorcloudy
Copy link
Member Author

@fmeum @Wyverald I finally made this work, the windows part is a bit annoying. Please take a look!

site/en/external/vendor.md Outdated Show resolved Hide resolved
*/
private void replantSymlinks(Path repoUnderVendor, Path externalRepoRoot) throws IOException {
try {
Collection<Path> symlinks = FileSystemUtils.traverseTree(repoUnderVendor, Path::isSymbolicLink);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we inline this into the moveTree file system traversal to avoid another one?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, I have to re-implement the moveTreesBelow to inline this. I'm not so convinced of the trade off between the performance gain and the duplicated code. Do you really think this optimization very important?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not at this point, no. But maybe we could add some profiler spans?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point! Profile added.

.getRelative(EXTERNAL_ROOT_SYMLINK_NAME)
.getRelative(target.relativeTo(externalRepoRoot.asFragment()));
if (OS.getCurrent() == OS.WINDOWS){
// On Windows, FileSystemUtils.ensureSymbolicLink always resolves paths to absolute path.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That seems error-prone, could we fix its implementation instead?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed, I did a few test on Windows, it seems even long paths work with relative paths if resolved properly. But still, I prefer to implement this in a follow-up change, so that we can rollback easily if things go wrong. WDYT?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, just find out that junctions can only be absolute paths: https://superuser.com/questions/343074/directory-junction-vs-directory-symbolic-link

Also verified on Windows

pcloudy@BAZEL-WINDOWS-P C:\Users\pcloudy\workdir\junction_test>mklink /J foo bar
Junction created for foo <<===>> bar

pcloudy@BAZEL-WINDOWS-P C:\Users\pcloudy\workdir\junction_test>dir
 Volume in drive C has no label.
 Volume Serial Number is 80B1-D386

 Directory of C:\Users\pcloudy\workdir\junction_test

06/17/2024  08:22 PM    <DIR>          .
06/17/2024  08:22 PM    <DIR>          ..
06/17/2024  08:22 PM    <JUNCTION>     foo [C:\Users\pcloudy\workdir\junction_test\bar]
               0 File(s)              0 bytes
               3 Dir(s)   8,777,269,248 bytes free

That means it's gonna be hard to fix FileSystemUtils.ensureSymbolicLink since if the target is a relative path pointing to a directory, then we have to decide which property to prioritize. Choosing relative path means we need admin right (or developer mode) to create actual symlink, choosing junction means we lose the relative path. And the second choice doesn't work for our use case.

// On Windows, FileSystemUtils.ensureSymbolicLink always resolves paths to absolute path.
// Use Files.createSymbolicLink here instead to preserve relative target path.
symlink.delete();
Files.createSymbolicLink(java.nio.file.Path.of(symlink.getPathString()), java.nio.file.Path.of(newTarget.getPathString()));
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are we also rewriting junctions to symlinks here? I'm asking because the former wouldn't require admin rights, but the latter may.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nope, the Files. class doesn't offer any way to create Junction via API. But if we can solve the FileSystemUtils.ensureSymbolicLink problem, it should be able to do that.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since junction only supports absolute path, this has to be a symlink. I updated the doc to mention this.

@github-actions github-actions bot removed the awaiting-review PR is awaiting review from an assigned reviewer label Jun 18, 2024
meteorcloudy added a commit to meteorcloudy/bazel that referenced this pull request Jun 18, 2024
To make sure symlinks work correctly, Bazel uses the following strategy to rewrite symlinks in the vendored source:

  - Create a symlink `<vendor_dir>/bazel-external` that points to `$(bazel info output_base)/external`. It is refreshed by every Bazel command automatically.
  - For the vendored source, rewrite all symlinks that originally point to a path under `$(bazel info output_base)/external` to a relative path under `<vendor_dir>/bazel-external`.

Fixes bazelbuild#22303

Closes bazelbuild#22723.

PiperOrigin-RevId: 644349481
Change-Id: I853ac0ea5405f0cf58431e988d727e690cbbb013
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
team-Documentation Documentation improvements that cannot be directly linked to other team labels team-ExternalDeps External dependency handling, remote repositiories, WORKSPACE file.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Vendor mode does not remap cross-repo symlinks to vendor dir
2 participants