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

stdenv: add applyPatches and support patch directories #335579

Open
wants to merge 4 commits into
base: staging
Choose a base branch
from

Conversation

tie
Copy link
Member

@tie tie commented Aug 18, 2024

Description of changes

This PR adds applyPatches function to the stdenv that supports patch directories and refactors the default patchPhase to use it. Also documents applyPatches trivial builder in Nixpkgs manual.

Things done

  • Built on platform(s)
    • x86_64-linux
    • aarch64-linux
    • x86_64-darwin
    • aarch64-darwin
  • For non-Linux: Is sandboxing enabled in nix.conf? (See Nix manual)
    • sandbox = relaxed
    • sandbox = true
  • Tested, as applicable:
  • Tested compilation of all packages that depend on this change using nix-shell -p nixpkgs-review --run "nixpkgs-review rev HEAD". Note: all changes have to be committed, also see nixpkgs-review usage
  • Tested basic functionality of all binary files (usually in ./result/bin/)
  • 24.11 Release Notes (or backporting 23.11 and 24.05 Release notes)
    • (Package updates) Added a release notes entry if the change is major or breaking
    • (Module updates) Added a release notes entry if the change is significant
    • (Module addition) Added a release notes entry if adding a new NixOS module
  • Fits CONTRIBUTING.md.

Add a 👍 reaction to pull requests you find important.

@github-actions github-actions bot added 8.has: documentation This PR adds or changes documentation 6.topic: stdenv Standard environment labels Aug 18, 2024
@tie tie mentioned this pull request Aug 18, 2024
13 tasks
@tie tie force-pushed the stdenv-patch-dir branch from 00b0619 to 5298ac1 Compare August 18, 2024 11:11
@philiptaron philiptaron self-requested a review August 19, 2024 00:09
@philiptaron
Copy link
Contributor

philiptaron commented Aug 19, 2024

I'm of two minds about this PR.

On the one hand, I like the way that applyPatches is separated from the patchPhase. I like the way it's upgraded with arrays and all.

On the other hand, as a reviewer, I don't like how it's harder to validate which patches get applied. I like that I can be assured that adding a patch means I get a cue in the .nix file.

Adding a patch file and "magically" having it be applied feels not great to me.

Maybe my intuition is off here. I'd love to here from others.

@tie
Copy link
Member Author

tie commented Aug 19, 2024

Edit: related to https://savannah.gnu.org/bugs/?57717

I really have a bad luck for encountering bugs in every piece of software I touch… I’m adding tests for an existing applyPatches trivial builder (and applyPatches function by extension) and the following test case fails (as expected) because patch segfaults (not expected but the test succeeds because we expect failure).

  patchWithIncorrectFilePath = testers.testBuildFailure (applyPatches {
    src = writeTextDir "file1.txt" "content";
    patches = [
      (writeText "incorrect-path.patch" ''
        diff --git a/file2.txt b/file2.txt
        index e69de29..d20e9cd 100644
        --- a/file2.txt
        +++ b/file2.txt
        @@ -1 +1 @@
        -old content
        +new content
      '')
    ];
  })

Standalone reproducer:

$ uname -a
Linux utm 6.6.32 #1-NixOS SMP Sat May 25 14:22:56 UTC 2024 aarch64 GNU/Linux
$ uname -a
Linux saitama 6.6.32 #1-NixOS SMP PREEMPT_DYNAMIC Sat May 25 14:22:56 UTC 2024 x86_64 GNU/Linux
$ patch --version
GNU patch 2.7.6
(GNU copyright notice omitted)
$ patch -p1 <<'EOF'
diff --git a/file2.txt b/file2.txt
index e69de29..d20e9cd 100644
--- a/file2.txt
+++ b/file2.txt
@@ -1 +1 @@
-old content
+new content
EOF

Spams

patch: **** Can't create file file2.txt.orig : Too many open files
patch: **** Can't create file file2.txt.orig : Too many open files
patch: **** Can't create file file2.txt.orig : Too many open files
patch: **** Can't create file file2.txt.orig : Too many open files
patch: **** Can't create file file2.txt.orig : Too many open files
patch: **** Can't create file file2.txt.origSegmentation fault (core dumped)

To be fair, the patch file for this test case was generated by LLM, and patch doesn’t crash without Git headers.

@tie
Copy link
Member Author

tie commented Aug 19, 2024

On the other hand, as a reviewer, I don't like how it's harder to validate which patches get applied. I like that I can be assured that adding a patch means I get a cue in the .nix file.

An existing patch file can be updated to add more changes without updating package’s .nix file, similar to adding a file in a directory. With patch set directories, Nix files don’t contain a complete list of patch files, but I don’t think a full list of patch files under patches attribute is useful without actual patch contents and context (and thus encourages duplication between patch message and comments above patch paths).

Patch directories are useful for managing patches using tools like git (am, format-patch) and quilt. Unless series file exists, I think it’s easier to validate which patches are applied: all patches in a directory (with .patch extension, optionally compressed). E.g. I’ve seen packages that had unused .patch files that were not removed after version updates like 5bf99d0.

Also, this is useful for applying patch sets from other derivations, e.g. using fetchurl from Debian (that uses series file from quilt, see https://wiki.debian.org/debian/patches and https://wiki.debian.org/UsingQuilt, example: https://salsa.debian.org/debian/bash/-/blob/6a0146056618a32be35ba89e1b092eda2f2fa749/debian/patches/series). This use case currently requires either IFD and custom patch phase.

That is, for simple one-off patches in Nixpkgs, I’d expect maintainers to continue adding individual patch file paths to the patches argument, but I think directories make it easier to manage larger sets of patches that are required for some projects and allow using patch management tools.

@tie tie force-pushed the stdenv-patch-dir branch 2 times, most recently from 7bd20ea to a822353 Compare August 19, 2024 09:09
@ofborg ofborg bot added the 8.has: package (new) This PR adds a new package label Aug 19, 2024
@tie tie force-pushed the stdenv-patch-dir branch from a822353 to f7f5ba7 Compare August 19, 2024 13:09
@tie
Copy link
Member Author

tie commented Aug 19, 2024

Pushed a commit to fix #335579 (comment).

This PR ready for review. Let me know if I should open separate PRs for 66082f9814e2085d90b0df4b940664f886523d54 and e245b88996d8bbf6939ab37aeb18f675cbc2e91e.

@tie tie marked this pull request as ready for review August 19, 2024 14:37
@emilazy
Copy link
Member

emilazy commented Aug 19, 2024

Seems like a reasonable change to me, and the Bash code seems high quality as always. I take it the series format is compatible with how it is used in Debian, etc.? I’m curious if we have an immediate use case for that, but it seems like sensible functionality and doesn’t add much weight. I’d rather not see a strong proliferation of maintaining forks of packages as patch sets in the Nixpkgs tree, but to the extent that we’re going to, this seems like a clean way of doing it.

I’m wondering if we should require a series file when directories are used. I suspect people are not necessarily going to be disciplined about the filename ordering of various patches and it could cause headaches.

Copy link
Contributor

@philiptaron philiptaron left a comment

Choose a reason for hiding this comment

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

Requesting changes for suspected inode order nondeterminism in patch application.

I really like most of this patch and I'm letting the directory application thing marinate. Appreciate the discussion.

Comment on lines +1153 to +1155
When processing a directory without a `series` file, only files with standard
patch extensions `.patch` and `.diff` will be considered for application,
optionally with compressed file name extension (e.g. `.patch.gz`).
Copy link
Contributor

Choose a reason for hiding this comment

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

Will they be sorted or is there non-determinism based on inode order? 👀

Copy link
Member Author

Choose a reason for hiding this comment

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

See #335579 (comment)

I agree with #335579 (comment), I’ll update applyPatches to return an error if series file does not exist to avoid implicit ordering.

Copy link
Contributor

Choose a reason for hiding this comment

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

Requiring a series file addresses this possibility definitively. I can get on board with that.

Comment on lines +840 to +832
# Docs in doc/build-helpers/trivial-build-helpers.chapter.md
# See https://nixos.org/manual/nixpkgs/unstable/#trivial-builder-applyPatches
Copy link
Contributor

Choose a reason for hiding this comment

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

Awesome, thank you.

Copy link
Contributor

Choose a reason for hiding this comment

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

This test file is super high quality. Thank you @tie (and I heartily co-sign @emilazy's nomination for committer rights.)

Comment on lines +1333 to +1342
# We support a simple series file with a list of patches. Empty
# lines and comments are ignored. Guards-like conditions (see
# guards(1) from quilt) are explicitly forbidden to avoid breaking
# changes if support is added in the future.
Copy link
Contributor

Choose a reason for hiding this comment

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

Stupid line breaking suggestions to avoid enjambment.

Suggested change
# We support a simple series file with a list of patches. Empty
# lines and comments are ignored. Guards-like conditions (see
# guards(1) from quilt) are explicitly forbidden to avoid breaking
# changes if support is added in the future.
# We support a simple series file with a list of patches.
# Empty lines and comments are ignored. Guards-like conditions
# (see guards(1) from quilt) are explicitly forbidden
# to avoid breaking changes if support is added in the future.

Comment on lines +1354 to +1361
# NB nullglob is set in stdenv.
patchFiles+=( "$p"/*.{patch,diff}{,.gz,.bz2,.xz,.lzma} )
Copy link
Contributor

Choose a reason for hiding this comment

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

Here's where I suspect that inode order non-determinism is creeping in.

Copy link
Member Author

Choose a reason for hiding this comment

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

Bash filename expansions are sorted alphabetically.
https://www.gnu.org/software/bash/manual/html_node/Filename-Expansion.html#:~:text=If%20one%20of%20these%20characters%20appears,matching%20the%20pattern

This is locale dependent (not an issue in Nix sandbox) but not affected by inode order.

Copy link
Member

Choose a reason for hiding this comment

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

That might be painful for nix develop. We should probably set locale variables for that, I guess…

I’d consider this kind of thing another (weak) argument for requiring a series file.

Copy link
Contributor

Choose a reason for hiding this comment

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

Ugh, locales. This pushes me over the line into series-file-required land.

Copy link
Member Author

Choose a reason for hiding this comment

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

Copy link
Contributor

Choose a reason for hiding this comment

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

you could sort it after the fact?

@@ -0,0 +1,51 @@
From b7b028a77bd855f6f56b17c8837fc1cca77b469d Mon Sep 17 00:00:00 2001
From: Andreas Gruenbacher <agruen@gnu.org>
Date: Fri, 28 Jun 2019 00:30:25 +0200
Copy link
Contributor

Choose a reason for hiding this comment

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

Has there not been a patch release since this!?

https://git.savannah.gnu.org/cgit/patch.git shows the answer is yes: 2.7.6 was tagged 7 years ago.

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, no releases for 7 years, Debian applies this patch as well.

Copy link
Member

Choose a reason for hiding this comment

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

I would be in favour of bumping to a Git revision given the number of fixes since then.

I think we should split out the two commits you mentioned into separate PRs so we can discuss things like that.

Copy link
Contributor

Choose a reason for hiding this comment

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

Yes, absolutely agreed that a git version is the right call vs. vendoring the accumulated patches.

Copy link
Member

Choose a reason for hiding this comment

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

https://git.savannah.gnu.org/cgit/patch.git/commit/?id=dce4683cbbe107a95f1f0d45fabc304acfb5d71a looks like the only potentially‐breaking interface change at a glance, but we can probably handle it. We have so many patches for patch right now and five of them are CVEs, we should just bump and nag upstream to cut a release.

(But yeah let’s talk about this on another PR!)

@emilazy
Copy link
Member

emilazy commented Aug 19, 2024

Another potential reason to require a series file is that not every patch will necessarily have a full header, and ones that are imported from upstream will likely have the headers from the upstream commits without a Nixpkgs‐specific rationale. Ensuring we always have a file where we can attach comments to patches would encourage best practices.

@philiptaron
Copy link
Contributor

The one downside of the series file is that you can delete the entry in the series and orphan a patch file in the repository. Removing that possibility was one reason that this PR exists, as I understand it.

I do broadly agree with the sentiment that a series file is a good idea, following @emilazy's argumentation. But if it's a good idea, isn't it basically just another level of indirection on listing out the patch files in the package.nix file? Which then gets back to my original inclination, keeping the package.nix as the source of truth for which patch files are used and in what order they are applied.

@emilazy
Copy link
Member

emilazy commented Aug 19, 2024

The one downside of the series file is that you can delete the entry in the series and orphan a patch file in the repository. Removing that possibility was one reason that this PR exists, as I understand it.

Perhaps it could error out if there are files in the directory not mentioned in the series file. That would be restrictive, and maybe we’d regret it down the line, but it’d be easier to loosen the constraint than to add it later.

@ofborg ofborg bot requested review from midchildan and cdepillabout August 19, 2024 15:44
@emilazy
Copy link
Member

emilazy commented Aug 19, 2024

I’m ambivalent about the source‐of‐truth thing. It doesn’t feel too different to a Nix file importing another. Being able to use other tools to work with series files might be helpful. One thing this change lets us do that might be cool is adding an entire Debian package’s patches directory to patches, series and all. But on the other hand, who knows if we actually want to encourage that.

@philiptaron
Copy link
Contributor

OK, I can say ✅ to this with series file required in the directory case and a check that there aren't excess patches hanging about.

Think that's reasonable?

@wolfgangwalther
Copy link
Contributor

On the other hand, as a reviewer, I don't like how it's harder to validate which patches get applied. I like that I can be assured that adding a patch means I get a cue in the .nix file.

Adding a patch file and "magically" having it be applied feels not great to me.

Maybe my intuition is off here. I'd love to here from others.

I agree with that.

I can't find a better solution to support those series files, though.

It really comes down to this I guess:

One thing this change lets us do that might be cool is adding an entire Debian package’s patches directory to patches, series and all. But on the other hand, who knows if we actually want to encourage that.

I'm not sure.

@tie
Copy link
Member Author

tie commented Aug 20, 2024

Not sure about the additional check for excess patches. I’m OK with that, but not sure what a potential implementation should do. E.g. directory can have a series file that uses patches from subdirectories, https://salsa.debian.org/js-team/nodejs/-/tree/ae4f839ed39c83815d1da835fbc4852e0e6d9971/debian/patches. Performing a recursive check seems to be expensive.

I think it’s fine to omit the check. It’s a lot easier to miss a patch that should be removed when it’s stored alongside a bunch of other required files that are conditionally included from multiple Nix files. Patch set directory, on the other hand, is functionally equivalent to concatenating multiple patches into single file, and ideally managed with some tooling (be that quilt or manual git format-patch exports).

tie added 4 commits August 26, 2024 16:32
See https://savannah.gnu.org/bugs/?57717

Reproducer:

```console
$ patch -p1 <<'EOF'
diff --git a/file2.txt b/file2.txt
index e69de29..d20e9cd 100644
--- a/file2.txt
+++ b/file2.txt
@@ -1 +1 @@
-old content
+new content
EOF
```

```
(repeated lines omitted)
patch: **** Can't create file file2.txt.orig : Too many open files
patch: **** Can't create file file2.txt.orig : Too many open files
patch: **** Can't create file file2.txt.origSegmentation fault (core dumped)
```
@tie tie force-pushed the stdenv-patch-dir branch from 66082f9 to ef1aa00 Compare August 26, 2024 13:34
@wegank wegank added the 2.status: merge conflict This PR has merge conflicts with the target branch label Sep 10, 2024
@philiptaron
Copy link
Contributor

philiptaron commented Sep 24, 2024

@tie, this PR is sort of rotting on the vine. How'd you like to proceed here? Is there a way to get to unanimity and everyone saying ✅? Should we focus on getting #337981 #337961 through first?

@emilazy
Copy link
Member

emilazy commented Sep 24, 2024

(I think you mean another PR)

@tie
Copy link
Member Author

tie commented Sep 29, 2024

@philiptaron, sorry for the late reply, I’m taking a small break from open source contributions until October.
Yes, I’d like to complete #337961 before proceeding with this PR.

@emilazy
Copy link
Member

emilazy commented Sep 29, 2024

Take care; Nixpkgs will be here when you get back :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
2.status: merge conflict This PR has merge conflicts with the target branch 6.topic: stdenv Standard environment 8.has: documentation This PR adds or changes documentation 8.has: package (new) This PR adds a new package 10.rebuild-darwin: 501+ 10.rebuild-darwin: 5001+ 10.rebuild-darwin-stdenv This PR causes stdenv to rebuild 10.rebuild-linux: 501+ 10.rebuild-linux: 5001+ 10.rebuild-linux-stdenv This PR causes stdenv to rebuild
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants