{"id":984,"date":"2025-09-17T14:46:13","date_gmt":"2025-09-17T21:46:13","guid":{"rendered":"https:\/\/porkrind.org\/missives\/?p=984"},"modified":"2025-09-17T14:52:35","modified_gmt":"2025-09-17T21:52:35","slug":"texinfo-and-nixpgks","status":"publish","type":"post","link":"https:\/\/porkrind.org\/missives\/texinfo-and-nixpgks\/","title":{"rendered":"Texinfo and Nixpkgs"},"content":{"rendered":"<p>I spent a bunch of time trying to get <a href=\"https:\/\/www.gnu.org\/software\/texinfo\/\">Texinfo<\/a> to work correctly with Nix on macOS. I wanted my <code>nix-env<\/code> installed packages to have the info files and top level index file (called <code>dir<\/code>, confusingly \ud83d\ude42). I don&#8217;t really use the <code>info<\/code> cli tool, but I use Emacs and I want to view the <code>make<\/code> 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 <code>nix-env -i<\/code>.<\/p>\n<p>I started with <a href=\"https:\/\/gist.github.com\/LnL7\/570349866bb69467d0caf5cb175faa74\">something simple<\/a> but found <a href=\"https:\/\/gist.github.com\/LnL7\/570349866bb69467d0caf5cb175faa74?permalink_comment_id=3372828#gistcomment-3372828\">this comment<\/a> and ended up using that as a basis for <a href=\"https:\/\/github.com\/caldwell\/userPackages\/blob\/master\/default.nix\">what I&#8217;m currently using<\/a>.<\/p>\n<p>Nixpkgs separates some of their outputs so that you can choose which ones you want (&#8220;man&#8221;, &#8220;info&#8221;, &#8220;doc&#8221;, etc.). The first annoyance is that <code>nix-env<\/code> doesn&#8217;t include &#8220;info&#8221; outputs even when <code>texinfoInteractive<\/code> (the curses <code>info<\/code> viewer package) is installed. The solution to that was adding a custom package to my list of packages:<\/p>\n<pre><code class=\"language-nix\">      info-dir = let\n        all-but = excluding: (\n          super.lib.map\n            (name: (super.lib.getAttr name self.userPackages))\n            (super.lib.subtractLists excluding (super.lib.attrNames self.userPackages)));\n        all-but-me-and-info = all-but [ \"info-dir\" \"texinfoInteractive\" ];\n      in super.buildEnv {\n        name = \"info-dir\";\n        paths = all-but-me-and-info;\n        pathsToLink = [ \"\/share\/info\" ];\n        extraOutputsToInstall = [ \"info\" ];\n      };\n<\/code><\/pre>\n<p>This adds a package called <code>info-dir<\/code> which contains &#8220;info&#8221; outputs of all the packages I&#8217;ve selected and builds a symlink tree in its <code>$out\/share\/info<\/code> which <code>nix-env<\/code> then symlinks into my profile.<\/p>\n<p>However, this isn&#8217;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 <code>postBuild<\/code> to the <code>info-dir<\/code> package definition:<\/p>\n<pre><code class=\"language-nix\">        postBuild = ''\n          (set -x\n          if [ -x ${super.texinfoInteractive}\/bin\/install-info -a -w \"$out\/share\/info\" ]; then\n            shopt -s nullglob\n            for i in {$out,${super.texinfoInteractive}}\/share\/info\/*.info{,.gz}; do\n              ${super.texinfoInteractive}\/bin\/install-info \"$i\" $out\/share\/info\/dir\n            done\n          fi\n          )\n        '';\n<\/code><\/pre>\n<p>However, that <em>still<\/em> wasn&#8217;t enough! To my chagrin the <code>dir<\/code> file did not appear in <code>~\/.nix-profile\/share\/info\/dir<\/code>. But when I inspected the info-dir package in the nix-store, it <em>was there<\/em>! What?? So someone is stripping it off? Now I started digging through source code but I couldn&#8217;t find anything good in the <a href=\"https:\/\/github.com\/NixOS\/nixpkgs\">nixpkgs repo<\/a>: <code>info\/dir<\/code> only appears there once but in some random package I wasn&#8217;t using. Next I checked the <a href=\"https:\/\/github.com\/NixOS\/nix\">Nix binaries repo<\/a> and my git grep yielded this:<\/p>\n<p><a href=\"https:\/\/github.com\/NixOS\/nix\/blob\/3eb223f4bb584a7150e33683869f2ecd1a9f5845\/src\/libstore\/builtins\/buildenv.cc#L59\">src\/libstore\/builtins\/buildenv.cc:59<\/a><\/p>\n<pre><code class=\"language-c\">        \/* The files below are special-cased to that they don't show\n         * up in user profiles, either because they are useless, or\n         * because they would cause pointless collisions (e.g., each\n         * Python package brings its own\n         * `$out\/lib\/pythonX.Y\/site-packages\/easy-install.pth'.)\n         *\/\n        if (hasSuffix(srcFile, \"\/propagated-build-inputs\") || hasSuffix(srcFile, \"\/nix-support\")\n            || hasSuffix(srcFile, \"\/perllocal.pod\") || hasSuffix(srcFile, \"\/info\/dir\") || hasSuffix(srcFile, \"\/log\")\n            || hasSuffix(srcFile, \"\/manifest.nix\") || hasSuffix(srcFile, \"\/manifest.json\"))\n            continue;\n<\/code><\/pre>\n<p>Wait, it&#8217;s <em>hard coded<\/em> to ignore &#8220;dir&#8221; files in &#8220;info&#8221; 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 <code>git blame<\/code>, working back through the code as it was rewritten from Perl to C++ and finally found the original commit (from 2005!):<\/p>\n<pre>commit <a href=\"https:\/\/github.com\/NixOS\/nix\/commit\/3fae65d4ccea78eeb0aef5acb4b613a0547a4c6d\">3fae65d4ccea78eeb0aef5acb4b613a0547a4c6d<\/a>\nAuthor: Eelco Dolstra &lt;e.dolstra@tudelft.nl&gt;\nDate:   Fri Jul 22 20:37:39 2005 +0000\n\n    * Adhockery.\n\ndiff --git a\/corepkgs\/buildenv\/builder.pl.in b\/corepkgs\/buildenv\/builder.pl.in\nindex a1914f658..1597ffa29 100755\n--- a\/corepkgs\/buildenv\/builder.pl.in\n+++ b\/corepkgs\/buildenv\/builder.pl.in\n@@ -26,6 +26,7 @@ sub createLinks {\n        if ($srcFile =~ \/\\\/propagated-build-inputs$\/ ||\n             $srcFile =~ \/\\\/nix-support$\/ ||\n             $srcFile =~ \/\\\/perllocal.pod$\/ ||\n+            $srcFile =~ \/\\\/info\\\/dir$\/ ||\n             $srcFile =~ \/\\\/log$\/)\n         {\n             # Do nothing.\n<\/pre>\n<p>Sigh. That&#8217;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:<\/p>\n<p><a href=\"https:\/\/cgit.git.savannah.gnu.org\/cgit\/emacs.git\/tree\/lisp\/info.el?h=emacs-30.2&#038;id=636f166cfc86aa90d63f592fd99f3fdd9ef95ebd#n1412\">info.lisp:1412<\/a><\/p>\n<pre><code class=\"language-lisp\">          ;; Try several variants of specified name.\n          ;; Try upcasing, appending `.info', or both.\n          (let* (file\n             (attrs\n              (or\n               (progn (setq file (expand-file-name \"dir\" truename))\n                  (file-attributes file))\n               (progn (setq file (expand-file-name \"DIR\" truename))\n                  (file-attributes file))\n               (progn (setq file (expand-file-name \"dir.info\" truename))\n                  (file-attributes file))\n               (progn (setq file (expand-file-name \"DIR.INFO\" truename))\n                  (file-attributes file))\n               ;; Shouldn't really happen, but sometimes does,\n               ;; eg on Debian systems with buggy packages;\n               ;; so may as well try it.\n               ;; https:\/\/lists.gnu.org\/r\/emacs-devel\/2012-03\/msg00005.html\n               (progn (setq file (expand-file-name \"dir.gz\" truename))\n                  (file-attributes file)))))\n<\/code><\/pre>\n<p>Aha! It did! They try some capitalization and also <code>dir.info<\/code>. So I changed my <code>postInstall<\/code> to this:<\/p>\n<pre><code class=\"language-nix\">        postBuild = ''\n          (set -x\n          if [ -x ${super.texinfoInteractive}\/bin\/install-info -a -w \"$out\/share\/info\" ]; then\n            shopt -s nullglob\n            for i in {$out,${super.texinfoInteractive}}\/share\/info\/*.info{,.gz}; do\n              ${super.texinfoInteractive}\/bin\/install-info \"$i\" $out\/share\/info\/dir.info\n            done\n          fi\n          )\n        '';\n<\/code><\/pre>\n<p>And <code>dir.info<\/code> slipped through! Yay! It appears that both <code>info<\/code> and <code>Emacs<\/code> see <code>dir.info<\/code>, so the solution is universal to both.<\/p>\n<p>Finally, &#8220;Make&#8221; shows up when I do <code>C-h i<\/code> in Emacs. Yay!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>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 \ud83d\ude42). I don&#8217;t really use the info cli tool, but I use Emacs and I want to view the &hellip; <a href=\"https:\/\/porkrind.org\/missives\/texinfo-and-nixpgks\/\" class=\"more-link\">Continue reading <span class=\"screen-reader-text\">Texinfo and Nixpkgs<\/span><\/a><\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[1],"tags":[],"class_list":["post-984","post","type-post","status-publish","format-standard","hentry","category-uncategorized"],"jetpack_featured_media_url":"","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/porkrind.org\/missives\/wp-json\/wp\/v2\/posts\/984","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/porkrind.org\/missives\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/porkrind.org\/missives\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/porkrind.org\/missives\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/porkrind.org\/missives\/wp-json\/wp\/v2\/comments?post=984"}],"version-history":[{"count":23,"href":"https:\/\/porkrind.org\/missives\/wp-json\/wp\/v2\/posts\/984\/revisions"}],"predecessor-version":[{"id":1007,"href":"https:\/\/porkrind.org\/missives\/wp-json\/wp\/v2\/posts\/984\/revisions\/1007"}],"wp:attachment":[{"href":"https:\/\/porkrind.org\/missives\/wp-json\/wp\/v2\/media?parent=984"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/porkrind.org\/missives\/wp-json\/wp\/v2\/categories?post=984"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/porkrind.org\/missives\/wp-json\/wp\/v2\/tags?post=984"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}