#+TITLE: Installing applications under =$HOME= One day I woke up and decided to stop letting ~make install~ put programs under =/usr/local/=. Not every project provides an =uninstall= target, so over time, as more and more applications get lumped under that hierarchy, it becomes a clutter of stuff that I cannot remove without… 1. guesstimating what files belong to any given program using timestamps, 2. weighing my odds of passing correctly crafted globs to the likes of =sudo find -delete= and =sudo rm -r= without destroying my system. Instead I figured I would start ~./configure~'ing programs with =--prefix=${HOME}/apps/${program}=, and teach whichever dotfiles manage session environment variables to arrange for installed resources to be found. How hard could it be? * =PATH= The Correct Way™ to set environment variables seems to depend on 1. the kind of session (TTY vs GUI), 2. the init system (e.g. [[https://www.freedesktop.org/software/systemd/man/environment.d.html][systemd user environment variables]]) 3. the desktop environment (e.g. [[https://userbase.kde.org/Session_Environment_Variables][Plasma session environment variables]]), 4. the display server, 5. the distro? - e.g. Debian's SSDM [[https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=794419][used not to]] source =~/.profile=, - and [[https://github.com/sddm/sddm/issues/448][developers said]] it should not anyway, - except [[https://github.com/sddm/sddm/issues/1551][it has provisions to do so]]? 6. [[https://man7.org/linux/man-pages/man8/pam_env.8.html][PAM]]? Empirically with Plasma on both Tumbleweed (20230929) and Debian 12, =~/.profile= seems to be run for both TTY and GUI sessions. Unclear what arcane magic is causing that: probably the login manager - on Tumbleweed =/usr/share/sddm/scripts/Xsession= seems to start a ~bash --login~, but getting lost in the =/usr/etc/X11/xdm/= weeds trying to track down who might source that =Xesssion= script. =~/.profile= it is, then 🤞 (For other DEs on X, my recollection was that =~/.xsessionrc= is the way to go for GUI sessions? No clue about Wayland) * ~man~ pages =manpath(5)= says: #+begin_quote By default, man-db examines the user's =$PATH=. For each =path_element= found there, it adds =manpath_element= to the search path. If there is no =MANPATH_MAP= line in the configuration file for a given =path_element=, then it adds all of =path_element/../man=, =path_element/man=, =path_element/../share=man=, and =path_element/shared/=man= that exist as directories to the search path. #+end_quote How convenient! Setting =PATH= automatically takes care of ~man~ then… unless =MANPATH= is set, in which case that logic is bypassed. ** openSUSE =/usr/etc/profile.d/manpath.sh= [fn:: sourced by =/etc/profile=, sourced by =/usr/etc/profile= and =~/.profile=.] plays a little danse to (a) query ~manpath~, which runs the heuristic described above (b) set the resulting value "in stone" by exporting =MANPATH=. This means that ~man~ will not consider any =PATH= element added by the user after that profile fragment was sourced. Solutions: 1. Unset =MANPATH= in =~/.profile=, after =/etc/profile= is read. =~/.profile= is "read each time a login shell is strated. All other interactive shells will only read .bashrc", so unclear whether that will affect graphical sessions. 2. Extend =PATH= with apps /before/ those profile fragments are loaded. Unclear how to accomplish that: - [[https://wiki.archlinux.org/title/environment_variables#Per_user][systemd user environment variables]]? Seems to be limited to =KEY=VALUE= directives; I would like to run a bit of logic that will automatically add every =~/apps/*/bin= directory. - [[https://userbase.kde.org/Session_Environment_Variables][Plasma session environment variables]]? Presumably will not affect TTYs? 3. Embrace the distro's method: if =MANPATH= is set, extend it. Went with solution 1 because empirically =~/.profile= seems to be sourced for GUI sessions (as noted in § =PATH=). * ~info~ pages Similar to ~man~, ~info~ knows to work with =PATH=. The priority [[https://git.savannah.gnu.org/cgit/texinfo.git/tree/info/infopath.c?h=texinfo-7.0.3#n41][seems to be]]: 1. the =INFOPATH= environment variable, 2. the =INFODIR= build macro, /if =infopath-no-defaults= is not set/, 3. the =DEFAULT_INFOPATH= build macro, /if =INFOPATH= is unset or ends with a trailing colon/. Any of these can contain the =PATH= literal, which means "iterate over =${PATH}= and add nearby =share/info= & =info= directories". By default, =DEFAULT_INFOPATH= contains =PATH= 🥳 but =INFODIR= has priority and is set to something boring like =/usr/share/info= 😒 Solution: #+begin_src bash cat < ~/.infokey #var # Disable INFODIR; fall back to DEFAULT_INFOPATH which prioritizes # program PATH over /usr/share/info. infopath-no-defaults=On EOF #+end_src * TODO ~bash~ completions * TODO Desktop entry files Prepend =${HOME}/apps/${program}/share= to =XDG_DATA_DIRS=? * TODO systemd services See systemd.unit(5) * Putting it all together Presenting =~/apps/activate=, to be sourced from =~/.profile=: #+begin_src bash # Hey Emacs; this is a -*- shell-script -*-. # Hopefully invoked by bash 🤞 _apps_dir=$(dirname ${BASH_SOURCE}) _apps_PATH=$( shopt -s nullglob bins=( "${_apps_dir}"/*/bin ) IFS=: eval 'echo "${bins[*]}"' ) export PATH=${_apps_PATH}:${PATH} #+end_src * Resources ** [[https://nullprogram.com/blog/2017/06/19/][nullprogram.com]] — Building and Installing Software in $HOME Explains how to set things up so that one can use =~/.local= as a first-class citizen for applications, libraries and development resources (headers, pkg-config, manpages).