Texinfo and Nixpkgs

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:

info.lisp:1412

          ;; 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!

Last Modified on: Dec 31, 2014 18:59pm