Thanks for the report!
I tried to reproduce this with your example, but I'm having trouble getting it to freeze. I'm using the same versions of tup & LilyPond:
$ tup
[ tup ] [0.016s] Scanning filesystem...
[ tup ] [0.031s] Reading in new environment variables...
[ tup ] [0.047s] Parsing Tupfiles...
0) [0.000s] .
[ ] 100%
[ tup ] [0.047s] No files to delete.
[ tup ] [0.047s] Generating .gitignore files...
[ tup ] [0.062s] Executing Commands...
0) [0.906s] lilypond.exe
test.lyGNU LilyPond 2.22.1
Processing `
test.ly'
Parsing...
test.ly:1: warning: no \version statement found, please add
\version "2.22.1"
for future compatibility
Interpreting music...
Preprocessing graphical objects...
Finding the ideal number of pages...
Fitting music on 1 page...
Drawing systems...
Converting to `test.pdf'...
Success: compilation successfully completed
[ ] 100%
[ tup ] [0.984s] Updated.
I did need to add some extra exclusions to ignore the LilyPond & fontconfig fonts caches:
: *.ly |> lilypond.exe %f |> %B.pdf ^.log ^lilypond-fonts.cache ^.cache/fontconfig
But otherwise it appears to work fine in both a bourne shell and the cmd shell. Is it possible the gspawn-win32-helper-console.exe remains around as a server process on your machine, but not on mine? There was a similar problem with mspdbsrv.exe, where that process would get created as a long-running server process by the first tool that wanted to use it. If that was created under tup, then tup would wait around forever for it to exit. If that is the culprit, we may just need to add another check for tup to ignore injecting into that process.
However, while I do see gspawn-win32-helper-console.exe get used in ProcMon, it exits afterward and tup exits successfully. So, it's possible that there's a different issue.
Can you try running yours under ProcMon and seeing if there are any clues? Maybe filter on tup.exe and/or gspawn-win32-helper-console.exe and lilypond.exe to see why a subprocess might be hanging around. Sorry I don't have a better suggestion at the moment.
-Mike