-
Notifications
You must be signed in to change notification settings - Fork 1.5k
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
Replace #4505 with a different set of workarounds #4527
base: trunk
Are you sure you want to change the base?
Conversation
Maybe I'm missing something, but the use of This change tries to create a symlink chain such as:
While that's typical output, Bazel could use its CAS (content-addressable storage) cache. In this model, Bazel's promise is to provide something which matches the checksum of the regular file, not the file/symlink structure (this is bazelbuild/bazel#23620). This is also true of
Note that things like remote caching can be a lot firmer about removing indirections, even more than the local cache. That's why we need bazelbuild/bazel#23620 to be fixed if we want to use symlinks with Bazel, since it's about retaining symlink structure. As far as alternative workarounds:
So that's why I went with something like the 2nd workaround: scripts rather than symlinks. I believe Google's Bazel support makes much more eager decisions about when to reuse cache content, and how to structure symlinks. If you want to go this route, perhaps it would make sense to verify that it works robustly using your access to Google's infrastructure? |
Oh, and to be sure, I believe that the main difference prior to #4505 is in changing the name "run_carbon" to "run/carbon". But since it's going back to symlinks, I do think that this is brittle. Maybe I could have been clearer in-person, but I think any solution using symlinks is going to require a split implementation: one for this repository on GitHub, one for Google. |
Yes, it was trying to couple this change with the change to prevent a symlink for the busybox itself. But thanks for the context on worrying about remote caches forcing even the original busybox to be a symlink into some CAS or other thing that loses all the interesting context. That was what I was missing that #4505 was trying to work around. Sorry if it wasn't clear -- this PR was mostly a question, I wasn't at all confident. I have another idea that I'm going to play with, but not sure it will work. But now I know how to test that. |
42849ee
to
d751db4
Compare
This restores the symlinks for the installation, but teaches the busybox info search to look for a relative path to the busybox binary itself before walking through symlinks. This let's it find the tree structure when directly invoking `prefix_root/bin/carbon` or similar, either inside of a Bazel rule or from the command line, and mirrors how we expect the installed tree to look. This works even when Bazel resolves the symlink target fully, and potentially to something nonsensical like a CAS file. In order to make a convenient Bazel target that can be used with `bazel run //toolchain`, this adds an override to explicitly set the desired argv[0] to use when selecting a mode for the busybox and a busybox binary. Currently, the workaround uses an environment variable because that required the least amount of plumbing, and seems a useful override mechanism generally, but I'm open to other approaches. This should allow a few things to work a bit more nicely: - It should handle sibling symlinks like `clang++` to `clang` or `ld.lld` to `lld`, where that symlink in turn points at the busybox. We want to use *initial* `argv[0]` value to select the mode there. - It avoids bouncing through Python (or other subprocesses) when invoking the `carbon` binary in Bazel rules, which will be nice for building the example code and benchmarking. It does come at a cost of removing one feature: the initial symlink can't be some unrelated alias like `my_carbon_symlink` -- we expect the *first* argv[0] name to have the meaningful filename for selecting a busybox mode. It also trades the complexity of the Python script for some complexity in the busybox search in order to look for a relative `carbon-busybox` binary. On the whole, I think that tradeoff is worthwhile, but it isn't free.
d751db4
to
f150e8b
Compare
Ok, after this helpful feedback and some offline discussions, I think a new attempt. The core of this is to look for a relative Should get us symlink overhead but be resilient to all the perplexities of symlinks (I hope!). PTAL! |
(Also, just flagging that I updated the PR description with new context.) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Understood you intend things to change, I still would appreciate tests demonstrating the behavior that's not intended to work anymore. It would make it clearer that it's an intended break, not an accidental one. (suggestions for specific things below)
@@ -166,5 +167,58 @@ TEST_F(BusyboxInfoTest, NotBusyboxSymlink) { | |||
EXPECT_FALSE(info.ok()); | |||
} | |||
|
|||
TEST_F(BusyboxInfoTest, LayerSymlinksInstallTree) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yesterday, one of the concerns I mentioned was incorrect install selection. You'd said you're okay with that, but would it be worth adding a test that demonstrates the expected result?
(note, you could share install tree creation via a helper with LayerSymlinksInstallTree
)
e.g., choosing "/opt" because that's a thing:
TEST_F(BusyBoxInfoTest, SymlinkAtSameLevelReturnsWrongInstall) {
// Just enough of the system install that incorrect looks can find the busybox.
MakeDir(dir_ / "lib");
MakeDir(dir_ / "lib/carbon");
MakeFile(dir_ / "lib/carbon/carbon-busybox");
// What it would look like if someone also did an install in "opt".
MakeDir(dir_ / "opt");
MakeDir(dir_ / "opt/lib");
MakeDir(dir_ / "opt/lib/carbon");
MakeFile(dir_ / "opt/lib/carbon/carbon-busybox");
MakeDir(dir_ / "opt/lib/carbon/llvm");
MakeDir(dir_ / "opt/lib/carbon/llvm/bin");
MakeSymlink(dir_ / "opt/lib/carbon/llvm/bin/clang", "../../carbon-busybox")
MakeDir(dir_ / "opt/bin");
MakeSymlink(dir_ / "opt/bin/carbon", "../lib/carbon/carbon-busybox")
auto target = MakeSymlink(dir_ / "opt/carbon", "bin/carbon");
auto info = GetBusyboxInfo(target.string());
ASSERT_TRUE(info.ok()) << info.error();
EXPECT_THAT(info->bin_path, Eq(dir_ / "opt/../lib/carbon/carbon-busybox"));
EXPECT_THAT(info->mode, Eq("clang"));
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good call. I've framed it more as a "positive" test that the busybox info finds a relative busybox before walking symlinks further. I think this reflects what you're looking for, but tell me if not.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Per discussion, I was trying to note the case of "/bin/carbon" versus "/opt/carbon" getting mixed up when "/opt/carbon" is pointing at an install under "/opt" instead of "/". I think the test is doing something different.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see, I've added dedicated testing for this scenario and checks to make sure we only do the relative path walk when in a bin
directory so its at least less likely to bump into this.
Co-authored-by: Jon Ross-Perkins <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for review, PTAL.
@@ -166,5 +167,58 @@ TEST_F(BusyboxInfoTest, NotBusyboxSymlink) { | |||
EXPECT_FALSE(info.ok()); | |||
} | |||
|
|||
TEST_F(BusyboxInfoTest, LayerSymlinksInstallTree) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good call. I've framed it more as a "positive" test that the busybox info finds a relative busybox before walking symlinks further. I think this reflects what you're looking for, but tell me if not.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think there was only one real outstanding issue here, but I know Jon is out for the next week so if someone else is up to picking up the last iteration on the review, I'd appreciate that. I think I've addressed Jon's comment here.
@@ -166,5 +167,58 @@ TEST_F(BusyboxInfoTest, NotBusyboxSymlink) { | |||
EXPECT_FALSE(info.ok()); | |||
} | |||
|
|||
TEST_F(BusyboxInfoTest, LayerSymlinksInstallTree) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see, I've added dedicated testing for this scenario and checks to make sure we only do the relative path walk when in a bin
directory so its at least less likely to bump into this.
toolchain/install/busybox_info.h
Outdated
// Returns the busybox information, given argv[0]. This primarily handles | ||
// resolving symlinks that point at the busybox. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm having trouble evaluating the test changes, because this comment doesn't really give me enough information to understand this function's desired behavior under the test conditions. Also, "This primarily handles resolving symlinks" doesn't seem accurate anymore.
After a fair amount of investigation, I think the key information that's missing here is that this function tries to find and return a bin_path
referring to a file named "carbon-busybox" that is part of the same Carbon install (as indicated by their paths) as argv0
or some file in the chain of symlinks from argv0
(which means we can presumably access the rest of the install by rewriting bin_path
). If that's right, I think it would help to document that as part of the contract.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure, updated comment a bit.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For what it's worth, I don't think the new comment is specific enough to have helped me understand the implementation or the tests (e.g. I don't think I'd know what "most consistent" means, and I particularly wouldn't expect it to mean that shallower symlink traversals are preferred). However, that level of specificity may not be what other readers need, especially when the function is doing something heuristic like this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've tried a new version of the comment. Is it any better?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just to be more explicit: I don't think this is blocking, because I'm not sure how much my experience as a reader generalizes, and I'm not sure how much effort it's worth putting into wordsmithing this API comment.
The previous revision worked better for me than this one, because it focused on the contract (albeit in imprecise terms), whereas this focuses on the implementation, and the implementation is complex enough that it's not clear what it implies about the contract.
If you want to keep iterating on this comment, maybe something like this?
"If argv0
is the path of a busyboxed executable that is part of a valid Carbon install, or indirectly refers to it via symlinks, then bin_path
will be the path of the lib/carbon/carbon-busybox
file in the same Carbon install."
I'm not sure that fully captures the intent, but that's the kind of contract-focused specificity that would have helped me.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure, tried wording much closer to that.
And it wasn't clear any of the comments were non-blocking, mostly trying to make progress here as this PR has been in review for a long time now.
// Starting from the second install uses the relative busybox rather than | ||
// traversing the symlink further. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this important? If they're all symlinks to the same file, won't the user get the same behavior either way?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The two installs might be different versions or unrelated.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't follow how that could be the case, given that the test seems to say that one of them is a collection of symlinks to the other.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We have two installs, let's say version v0.1 in /usr
and v0.2 in /usr/local
.
For some strange reason, there is also a symlink /usr/local/carbon
-> /usr/local/bin/carbon
.
There is no symlink from /usr/local/...
into /usr
(or vice versa).
The problem is that the symlink /usr/local/carbon
(which points to /usr/local/bin/carbon
and eventually to /usr/local/lib/carbon/carbon-busybox
) could use the same path-relative traversal as /usr/bin/carbon
and accidentally find /usr/lib/carbon/carbon-busybox
. The test is checking that it does not do this.
While we don't really intend symlinks to exist in this way, the goal is to avoid confusing behavior if it does occur.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It sounds like you're describing the behavior of RejectSymlinkInUnrelatedInstall
, not StopSearchAtFirstSymlinkWithRelativeBusybox
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, for some reason I thought that's where this comment thread was attached.
Because the new logic stops as soon as we see an install-relative busybox, we don't continue to follow symlinks when they, for example, link into a CAS directory tree or some other part of the Bazel output tree. This is what lets us use actual symlinks inside the prefix_root/...
file group even though Bazel fully resolves them rather than leave the relative symlink structure in tact: once we see the correctly shaped install tree, we stop traversing symlinks.
There is some explanation about this in the PR description, but let me know if I should add comments here as well.
Thanks, PTAL! |
This restores the symlinks for the installation, but teaches the busybox
info search to look for a relative path to the busybox binary itself
before walking through symlinks. This let's it find the tree structure
when directly invoking
prefix_root/bin/carbon
or similar, eitherinside of a Bazel rule or from the command line, and mirrors how we
expect the installed tree to look. This works even when Bazel resolves
the symlink target fully, and potentially to something nonsensical like
a CAS file.
In order to make a convenient Bazel target that can be used with
bazel run //toolchain
, this adds an override to explicitly set the desiredargv[0] to use when selecting a mode for the busybox and a busybox
binary. Currently, the workaround uses an environment variable because
that required the least amount of plumbing, and seems a useful override
mechanism generally, but I'm open to other approaches.
This should allow a few things to work a bit more nicely:
clang++
toclang
orld.lld
tolld
, where that symlink in turn points at the busybox.We want to use initial
argv[0]
value to select the mode there.invoking the
carbon
binary in Bazel rules, which will be nice forbuilding the example code and benchmarking.
It does come at a cost of removing one feature: the initial symlink
can't be some unrelated alias like
my_carbon_symlink
-- we expect thefirst argv[0] name to have the meaningful filename for selecting
a busybox mode.
It also trades the complexity of the Python script for some complexity
in the busybox search in order to look for a relative
carbon-busybox
binary. On the whole, I think that tradeoff is worthwhile, but it isn't
free.