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
stdenv: make symlinks that refer to the same output relative #74253
Conversation
Are there valid use cases where this is not desirable? I was thinking of providing an escape hatch if there are any... |
Are there valid use cases where this is not desirable? I was thinking of providing an escape hatch if there are any...
I guess a weird enough upstream build system of a reverse depedency could copy something to its own installation directory, which was OK when added (back when it was a plain file), works with absolute symlinks but might break for relative ones with some flags?
This sounds weird, but maybe something close is posible.
|
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 concept sounds nice, but I found two issues in the bash script.
Are there valid use cases where this is not desirable? I was thinking of providing an escape hatch if there are any...
I can definitely see issues with scripts that expect an absolute path from readlink
and will be broken by this change, so being able to disable this hook would be nice.
local relativeTarget | ||
while IFS= read -r -d $'\0' f; do | ||
symlinkTarget=$(readlink "$f") | ||
if [ "${symlinkTarget:0:${#prefix}}" != "$prefix" ]; then |
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.
This check has the potential to produce false negatives when two outputs share the same prefix:
/nix/store
├── 1111-out
│ └── file -> /nix/store/1111-out2/1234
└── 1111-out2
└── 1234
In this case /nix/store/1111-out/file
would be rewritten to file -> 1234
.
if [ "${symlinkTarget:0:${#prefix}}" != "$prefix" ]; then | |
if [[ "$symlinkTarget"/ != "$prefix"/* ]]; then |
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! While that technically should never happen within /nix/store
that might be a good safeguard.
I'll add that to the next version.
relativeTarget="${symlinkTarget:${#prefix}}" | ||
echo "rewriting symlink $f to be relative to $prefix" | ||
|
||
rm "$f" | ||
ln -rs "$prefix/$relativeTarget" "$f" |
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 ln
destination should be "$prefix/$f"
.
Never mind, f
is already absolute. Updated code block below.
To simplify the process this block can be replaced with these ln
flags:
echo "rewriting symlink $f to be relative to $prefix"
ln -snrf "$symlinkTarget" "$f"
as ln -r
produces the minimal possible relative link.
If we are going to find all symlinks and resolve them, it would be nice to fail the build with a meaningful error message if the target (file or dir at the location a symlink points to) does not exist.
… and also to provide and ways way to opt out of this behaviour. I do have some uses for symlinking to nonexistent files; for example when something wants to write to some path that is in store (but can be redirected to a proper location via a symlink)
|
Please note that I am only checking for references into the same output. So in general those shouldn't be the problem. You can still create arbitrary symlinks to directories that do not exist as long as they do not point into the output. Would that work for you? I feel like the enforcement of valid symlinks is yet another topic compared to what I wanted to accomplish here. |
Please note that I am only checking for references into the same output. So in general those shouldn't be the problem. You can still create arbitrary symlinks to directories that do not exist as long as they do not point into the output. Would that work for you?
Ah, right, same-output symlinks stay broken or valid after installation, and the situation where existence of a broken symlink to a nonexistent location is preferrable to plain absence of a directory entry sounds like a small component part of an unspeakable horror.
|
11a151e
to
c6c26e7
Compare
I added some of the review comments. I removed the hard failure on dangling symlinks and just left the warning in there. Does that make sense? I think failing on invalid symlinks is really a more complicated topic that we shouldn't try to solve right now. Warning is probably okay but failing is yet another different topic from this approach. We can add it to this hook in another PR once there is consensus about the desired behavior. |
@GrahamcOfBorg build bash |
Can this go in? |
While looking at the graph of all the outputs in my personal binary cache it became obvious that we have a lot of self references within the package set. That isn't an isuse by itself. However it increases the size of the binary cache for every (reproducible) build of a package that carries references to itself. You can no longer deduplicate the outputs since they are all unique. One of the ways to get rid of (a few) references is to rewrite all the symlinks that are currently used to be relative symlinks. Two build of something that didn't really change but carries a self-reference can the be store as the same NAR file again. I quickly hacked together this change to see if that would yield and success. My bash scripting skills are probably not great but so far it seem to somewhat work.
@FRidh Looks good to me :-) If @Ericson2314 is now happy I see no reason why we can't merge this. |
Thanks @FRidh ! |
@@ -0,0 +1,28 @@ | |||
fixupOutputHooks+=(_makeSymlinksRelative) |
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.
wlroots has
wlroots> +++ find /nix/store/3a0xwszw8n5dzzhsgfnilvsqi4hk565s-wlroots-0.15.1-examples -type l -print0
wlroots> find: '/nix/store/3a0xwszw8n5dzzhsgfnilvsqi4hk565s-wlroots-0.15.1-examples': No such file or directory
because the examples output is created in postFixup while this hook runs in fixupPhase
if this is changed to postFixupHooks
then we shouldn't get a error like that in many builds
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.
Motivation for this change
While looking at the graph of all the outputs in my personal binary
cache it became obvious that we have a lot of self references within the
package set. That isn't an isssue by itself. However it increases the
size of the binary cache for every (reproducible) build of a package
that carries references to itself. You can no longer deduplicate the
outputs since they are all unique. One of the ways to get rid of (a few)
references is to rewrite all the symlinks that are currently used to be
relative symlinks. Two buildd of something that didn't really change but
carry each a self-reference can the be stored as the same NAR file again.
I quickly hacked together this change to see if that would yield and
success. My bash scripting skills are probably not great but so far it
seem to somewhat work.
Things done
sandbox
innix.conf
on non-NixOS linux)./result/bin/
)nix path-info -S
before and after)Notify maintainers
cc @Ericson2314