I spent a bunch of time trying to get Texinfo to work correctly with Nix on macOS. I wanted my nix-env
installed packages to have the info files and top level index file (called dir
, confusingly 🙂). I don’t really use the info
cli tool, but I use Emacs and I want to view the make
info manual in particular. Getting it working was harder than I expected and I had to delve into way too much source code to figure it out. A couple years ago I set some overlays up so that my nix-env was declarative instead of adding packages ad-hoc with nix-env -i
.
I started with something simple but found this comment and ended up using that as a basis for what I’m currently using.
Nixpkgs separates some of their outputs so that you can choose which ones you want (“man”, “info”, “doc”, etc.). The first annoyance is that nix-env
doesn’t include “info” outputs even when texinfoInteractive
(the curses info
viewer package) is installed. The solution to that was adding a custom package to my list of packages:
info-dir = let
all-but = excluding: (
super.lib.map
(name: (super.lib.getAttr name self.userPackages))
(super.lib.subtractLists excluding (super.lib.attrNames self.userPackages)));
all-but-me-and-info = all-but [ "info-dir" "texinfoInteractive" ];
in super.buildEnv {
name = "info-dir";
paths = all-but-me-and-info;
pathsToLink = [ "/share/info" ];
extraOutputsToInstall = [ "info" ];
};
This adds a package called info-dir
which contains “info” outputs of all the packages I’ve selected and builds a symlink tree in its $out/share/info
which nix-env
then symlinks into my profile.
However, this isn’t enough by itself. Texinfo (and Emacs) want the top level index file. Nothing in nixpkgs makes that for us so we have to do it ourselves by adding a postBuild
to the info-dir
package definition:
postBuild = ''
(set -x
if [ -x ${super.texinfoInteractive}/bin/install-info -a -w "$out/share/info" ]; then
shopt -s nullglob
for i in {$out,${super.texinfoInteractive}}/share/info/*.info{,.gz}; do
${super.texinfoInteractive}/bin/install-info "$i" $out/share/info/dir
done
fi
)
'';
However, that still wasn’t enough! To my chagrin the dir
file did not appear in ~/.nix-profile/share/info/dir
. But when I inspected the info-dir package in the nix-store, it was there! What?? So someone is stripping it off? Now I started digging through source code but I couldn’t find anything good in the nixpkgs repo: info/dir
only appears there once but in some random package I wasn’t using. Next I checked the Nix binaries repo and my git grep yielded this:
src/libstore/builtins/buildenv.cc:59
/* The files below are special-cased to that they don't show
* up in user profiles, either because they are useless, or
* because they would cause pointless collisions (e.g., each
* Python package brings its own
* `$out/lib/pythonX.Y/site-packages/easy-install.pth'.)
*/
if (hasSuffix(srcFile, "/propagated-build-inputs") || hasSuffix(srcFile, "/nix-support")
|| hasSuffix(srcFile, "/perllocal.pod") || hasSuffix(srcFile, "/info/dir") || hasSuffix(srcFile, "/log")
|| hasSuffix(srcFile, "/manifest.nix") || hasSuffix(srcFile, "/manifest.json"))
continue;
Wait, it’s hard coded to ignore “dir” files in “info” directories with no way to override that??? Oh, come on. Well, perhaps the original commit can shed some light and suggest some work-around. So I went to work with git blame
, working back through the code as it was rewritten from Perl to C++ and finally found the original commit (from 2005!):
commit 3fae65d4ccea78eeb0aef5acb4b613a0547a4c6d Author: Eelco Dolstra <e.dolstra@tudelft.nl> Date: Fri Jul 22 20:37:39 2005 +0000 * Adhockery. diff --git a/corepkgs/buildenv/builder.pl.in b/corepkgs/buildenv/builder.pl.in index a1914f658..1597ffa29 100755 --- a/corepkgs/buildenv/builder.pl.in +++ b/corepkgs/buildenv/builder.pl.in @@ -26,6 +26,7 @@ sub createLinks { if ($srcFile =~ /\/propagated-build-inputs$/ || $srcFile =~ /\/nix-support$/ || $srcFile =~ /\/perllocal.pod$/ || + $srcFile =~ /\/info\/dir$/ || $srcFile =~ /\/log$/) { # Do nothing.
Sigh. That’s not very helpful at all. At this point I decided to check the Emacs info lisp out to see if it had any alternatives to the dir file:
;; Try several variants of specified name.
;; Try upcasing, appending `.info', or both.
(let* (file
(attrs
(or
(progn (setq file (expand-file-name "dir" truename))
(file-attributes file))
(progn (setq file (expand-file-name "DIR" truename))
(file-attributes file))
(progn (setq file (expand-file-name "dir.info" truename))
(file-attributes file))
(progn (setq file (expand-file-name "DIR.INFO" truename))
(file-attributes file))
;; Shouldn't really happen, but sometimes does,
;; eg on Debian systems with buggy packages;
;; so may as well try it.
;; https://lists.gnu.org/r/emacs-devel/2012-03/msg00005.html
(progn (setq file (expand-file-name "dir.gz" truename))
(file-attributes file)))))
Aha! It did! They try some capitalization and also dir.info
. So I changed my postInstall
to this:
postBuild = ''
(set -x
if [ -x ${super.texinfoInteractive}/bin/install-info -a -w "$out/share/info" ]; then
shopt -s nullglob
for i in {$out,${super.texinfoInteractive}}/share/info/*.info{,.gz}; do
${super.texinfoInteractive}/bin/install-info "$i" $out/share/info/dir.info
done
fi
)
'';
And dir.info
slipped through! Yay! It appears that both info
and Emacs
see dir.info
, so the solution is universal to both.
Finally, “Make” shows up when I do C-h i
in Emacs. Yay!