Using wrapProgram to prefix a command

I would like to prefix a command with another one which will then execute that command.
For example how lolcat turns the output of any following command into colored text.
Say, for example, what if I wanted to wrap the hello package with lolcat…
I would probably do this:

hello.overrideAttrs (old: {
  nativeBuildInputs = old.nativeBuildInputs ++ [ pkgs.lolcat ];

  installPhase = ''
    wrapProgram $out/bin/hello \
      --run lolcat
  '';
});

But that won’t run hello with lolcat, it will just run lolcat, which will print it’s help screen and then run hello without it.

I don’t think there’s an option for this.

The motive is a little unclear. Why is wrapProgram essential, here?

I guess writeShellScriptBin is a good fit. Then you just wrap. ${lib.getBin lolcat}/bin/lolcat ${lib.getBin hello}/bin/hello "$@"

wrapProgram does some magic that I am not sure how to replicate without it.
It calls the executable it wraps using the full path of it, which I am not sure how to access from a build phase of said package.

Specifically I want to wrap the java command in the jre package to make it execute using the primusrun command, which will make it execute using my beefy graphics card. (but only for a single other package, using nested overrides)

A shell script won’t do the trick since the java command is itself called by another executable in the package I am overriding.

It’s a modest magic, readily beaten back with a good flashlight.

At a high level, makeWrapper just moves ${blah}/bin/blah to ${blah}/bin/.blah-wrapped, and replaces ${blah}/bin/blah with a shell script (which sets some variables, and then runs ${blah}/bin/.blah-wrapped). Reading one of the wrappers it generates (i.e., $out/bin/hello from your example) may help to chase out the magic.

The jre package is most-likely just looking for “java” in its PATH (whether at build time or run time). It will probably use the first */bin/java it finds (this is a broad generalization; different language ecosystems do their own thing–but there’s almost certainly a mechanism you can hijack…)

You can probably re-wrap java using this information. You might also be able to invent a new package–perhaps named JavaScript–that outputs a shell script along the lines of what knedlsepp describes at the path $out/bin/java, and prepend it to jre’s inputs.

But how do I get the path of blah while building blah?
I cannot just do

{pkgs, ...}: {
  name = "blah";
  installPhase = "${self??}/bin/blah"
}

I cannot think of a way that would not cause infinite recursion…


Also, makeWrapper somehow gets the path of the bash package using @shell@. I have no idea how that works either. My best guess is that it uses some kind of template system.
https://github.com/NixOS/nixpkgs/blob/980ebd8f1effccb1dd63981dcd8bac132f27b203/pkgs/build-support/setup-hooks/make-wrapper.sh#L40

I wound up with this, but it feels kinda dirty:

      installPhase = installPhase + ''
        mv $out/bin/java $out/bin/.java-wrapped
        echo "#! ${pkgs.bash}/bin/bash" >> $out/bin/java
        echo "script_dir=\$(dirname \$(readlink --canonicalize-existing \$0))" >> $out/bin/java
        echo "export vblank_mode=0" >> $out/bin/java
        echo 'primusrun $script_dir/.java-wrapped "$@"' >> $out/bin/java
        chmod +x $out/bin/java
      '';

You could simply use wrapProgram make-wrapper.sh#L208

installPhase = (installPhase or "") + ''
  wrapProgram $out/bin/java --set script_dir '$(dirname $(readlink --canonicalize-existing "$0"))' --set vblank_mode 0 --run 'primusrun'
'';