apps.org (8381B)
1 #+TITLE: Installing applications under =$HOME= 2 3 One day I woke up and decided to stop letting ~make install~ put 4 programs under =/usr/local/=. Not every project provides an 5 =uninstall= target, so over time, as more and more applications get 6 lumped under that hierarchy, it becomes a clutter of stuff that I 7 cannot remove without… 8 9 1. guesstimating what files belong to any given program using 10 timestamps, 11 2. weighing my odds of passing correctly crafted globs to the likes of 12 =sudo find -delete= and =sudo rm -r= without destroying my system. 13 14 Instead I figured I would start ~./configure~'ing programs with 15 =--prefix=${HOME}/apps/${program}=, and teach whichever dotfiles 16 manage session environment variables to arrange for installed 17 resources to be found. 18 19 How hard could it be? 20 21 * =PATH= 22 The Correct Way™ to set environment variables seems to depend on 23 24 1. the kind of session (TTY vs GUI), 25 2. the init system (e.g. [[https://www.freedesktop.org/software/systemd/man/environment.d.html][systemd user environment variables]]) 26 3. the desktop environment (e.g. [[https://userbase.kde.org/Session_Environment_Variables][Plasma session environment variables]]), 27 4. the display server, 28 5. the distro? 29 - e.g. Debian's SSDM [[https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=794419][used not to]] source =~/.profile=, 30 - and [[https://github.com/sddm/sddm/issues/448][developers said]] it should not anyway, 31 - except [[https://github.com/sddm/sddm/issues/1551][it has provisions to do so]]? 32 6. [[https://man7.org/linux/man-pages/man8/pam_env.8.html][PAM]]? 33 34 Empirically with Plasma on both Tumbleweed (20230929) and Debian 12, 35 =~/.profile= seems to be run for both TTY and GUI sessions. Unclear 36 what arcane magic is causing that: probably the login manager - on 37 Tumbleweed =/usr/share/sddm/scripts/Xsession= seems to start a ~bash 38 --login~, but getting lost in the =/usr/etc/X11/xdm/= weeds trying to 39 track down who might source that =Xesssion= script. 40 41 =~/.profile= it is, then 🤞 42 43 (For other DEs on X, my recollection was that =~/.xsessionrc= is the 44 way to go for GUI sessions? No clue about Wayland) 45 46 * ~man~ pages 47 =manpath(5)= says: 48 49 #+begin_quote 50 By default, man-db examines the user's =$PATH=. For each 51 =path_element= found there, it adds =manpath_element= to the search 52 path. 53 54 If there is no =MANPATH_MAP= line in the configuration file for a 55 given =path_element=, then it adds all of =path_element/../man=, 56 =path_element/man=, =path_element/../share=man=, and 57 =path_element/shared/=man= that exist as directories to the search 58 path. 59 #+end_quote 60 61 How convenient! Setting =PATH= automatically takes care of ~man~ 62 then… unless =MANPATH= is set, in which case that logic is bypassed. 63 64 ** openSUSE 65 =/usr/etc/profile.d/manpath.sh= [fn:: sourced by =/etc/profile=, 66 sourced by =/usr/etc/profile= and =~/.profile=.] plays a little danse 67 to (a) query ~manpath~, which runs the heuristic described above (b) 68 set the resulting value "in stone" by exporting =MANPATH=. 69 70 This means that ~man~ will not consider any =PATH= element added by 71 the user after that profile fragment was sourced. 72 73 Solutions: 74 75 1. Unset =MANPATH= in =~/.profile=, after =/etc/profile= is read. 76 =~/.profile= says it is "read each time a login shell is started. 77 All other interactive shells will only read .bashrc", so unclear 78 whether that will affect graphical sessions. 79 2. Extend =PATH= with apps /before/ those profile fragments are 80 loaded. Unclear how to accomplish that: 81 - [[https://wiki.archlinux.org/title/environment_variables#Per_user][systemd user environment variables]]? Seems to be limited to 82 =KEY=VALUE= directives; I would like to run a bit of logic that 83 will automatically add every =~/apps/*/bin= directory. 84 - [[https://userbase.kde.org/Session_Environment_Variables][Plasma session environment variables]]? Presumably will not affect 85 TTYs? 86 3. Embrace the distro's method: if =MANPATH= is set, extend it. 87 88 Went with solution 1 because empirically =~/.profile= seems to be 89 sourced for GUI sessions (as noted in § =PATH=). 90 91 * ~info~ pages 92 Similar to ~man~, ~info~ knows to work with =PATH=. The priority 93 [[https://git.savannah.gnu.org/cgit/texinfo.git/tree/info/infopath.c?h=texinfo-7.0.3#n41][seems to be]]: 94 95 1. the =INFOPATH= environment variable, 96 2. the =INFODIR= build macro, /if =infopath-no-defaults= is not set/, 97 3. the =DEFAULT_INFOPATH= build macro, /if =INFOPATH= is unset or ends 98 with a trailing colon/. 99 100 Any of these can contain the =PATH= literal, which means "iterate over 101 =${PATH}= and add nearby =share/info= & =info= directories". By 102 default, 103 104 - =DEFAULT_INFOPATH= contains =PATH= 🥳 105 - *but* =INFODIR= has priority and is set to something boring like 106 =/usr/share/info= 😒 107 108 Solution: 109 110 #+begin_src bash 111 cat <<EOF > ~/.infokey 112 #var 113 # Disable INFODIR: fall back to DEFAULT_INFOPATH, which contains 114 # 'PATH', prioritizing program PATH over /usr/share/info. 115 infopath-no-defaults=On 116 EOF 117 #+end_src 118 119 * DONE systemd services 120 =systemd.unit(5)= § "User Unit Search Path" mentions a couple of 121 variables that we could append: 122 123 - =$XDG_CONFIG_DIRS/systemd/user/*= 124 - =$XDG_DATA_DIRS/systemd/user/*= 125 126 And: 127 128 #+begin_quote 129 When the variable =$SYSTEMD_UNIT_PATH= is set, the contents of this 130 variable overrides the unit load path. If =$SYSTEMD_UNIT_PATH= ends 131 with an empty component (=:=), the usual unit load path will be 132 appended to the contents of the variable. 133 #+end_quote 134 135 So we could either: 136 137 1. find the directory 𝒟 where an app tucks =systemd/user= in its 138 installation tree, and append 𝒟 to one of the XDG variables, 139 2. find the directory where an app tucks its =.service= files, and 140 append that directory to =SYSTEMD_UNIT_PATH=, keeping a final 141 colon. 142 143 (1) could feed multiple birds with one scone: in particular, 144 =XDG_DATA_DIRS= is used for other purposes (see § Desktop entry 145 files). Serendipitously, [[https://github.com/systemd/systemd/blob/1d87a00a951dca801c8ccd79c1460fa91efa7dce/src/basic/path-lookup.c#L147][the systemd sources]] suggest =DATA= over 146 =CONFIG= for our purposes: 147 148 #+begin_src c 149 /* Implement the mechanisms defined in 150 * 151 * https://standards.freedesktop.org/basedir-spec/basedir-spec-0.6.html 152 * 153 * We look in both the config and the data dirs because we 154 * want to encourage that distributors ship their unit files 155 * as data, and allow overriding as configuration. 156 */ 157 #+end_src 158 159 * DONE Desktop entry files 160 The Freedesktop [[https://specifications.freedesktop.org/menu-spec/menu-spec-latest.html][Desktop Menu]] and [[https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html][Desktop Entry]] specs spell out how 161 =XDG_DATA_DIRS= will be searched for =applications/*.desktop= files. 162 163 * TODO ~bash~ completions 164 165 * Putting it all together 166 Presenting [[./apps-demo/activate][=~/apps/activate=]], to be sourced from =~/.profile=: 167 168 #+INCLUDE: "apps-demo/activate" src sh 169 170 * Resources 171 ** [[https://nullprogram.com/blog/2017/06/19/][nullprogram.com]] — Building and Installing Software in $HOME 172 Explains how to set things up so that one can use =~/.local= as a 173 first-class citizen for applications, libraries and development 174 resources (headers, pkg-config, manpages). 175 176 ** [[https://specifications.freedesktop.org][specifications.freedesktop.org]] — Freedesktop.org Specifications 177 Hosts the specifications for the behavior and environment variables 178 most GNU/Linux distros (that I have experience with) adhere to, e.g. 179 180 - the [[https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html][Base Directory spec]] :: defines the various 181 =XDG_*_@(HOME|DIR|DIRS)= variables referenced here; 182 - the [[https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html][Desktop Entry spec]] :: explains the significance of a desktop 183 file's filename with respect to its position within =XDG_DATA_DIRS=; 184 - the [[https://specifications.freedesktop.org/menu-spec/menu-spec-latest.html][Desktop Menu spec]] :: defines the expected file locations for 185 desktop files (and more) with respect to XDG base directories. 186 187 ** [[https://wiki.archlinux.org/title/Environment_variables#Per_user][wiki.archlinux.org]] — Environment variables § Per user - ArchWiki 188 A thorough overview of the myriad of ways users can amend environment 189 variables, as examined in § PATH.