When downloading, compiling and installing applications from source, the installed programs ususally end up in /usr/local/bin, /usr/local/share, /usr/local/lib and other subdirectories of /usr/local. After a while /usr/local becomes messy. It is difficult to know which files belong to which package, and removing a package becomes a chore. Many packages have some kind of support for uninstalling (e.g. make uninstall), but not all.
There is a simple way of solving this problem. Instead of installing all packages to /usr/local, install each package to its own subdirectory of /usr/local, then symlink it to /usr/local/bin &c. A package can then be removed by a) removing its own unique subdirectory, and b) removing all dangling symbolic links.
If the source is in /usr/local/src/gcc-5.3.0, then put the resulting program in /usr/local/prog/gcc-5.3.0. This example assumes GNU configure:
$ cd /usr/local/src/gcc-5.3.0 $ ./configure --prefix=$(/bin/pwd | sed -e 's/src/prog/') $ make && make install
Then install into the regular places using symbolic links. GNU cp has an option -s for doing just that. Here is a script to automate installation:
#!/bin/bash -e usage() { echo "Usage: $(basename $0) source-dirs... target-dir" 1&>2 exit 1 } [ $# -gt 1 ] || { usage $0; } DESTDIR="${@: -1}" [ -d $DESTDIR/. ] || { echo "$0: Target directory $DESTDIR does not exist" 1&>2; exit 1; }), echo mkdir -p $DESTDIR/share for i in "${@:1:$(($#-1))}" do for d in bin etc include info lib man share/man; do if [ -d $i/$d ] ; then D=$(readlink -e $DESTDIR/`dirname $d`/.) echo cp -sfrv $i/$d $D/. fi done done
The odd-looking assignment DESTDIR="${@: -1}" extracts the very last argument on the command line. The even odder-looking for statement for i in "${@:1:$(($#-1))}" loops over all but the last argument. This makes the script behave as cp and mv, which take one or more source arguments, and a target argument at the end.
The script writes command to stdout.
linkin.sh /usr/local/prog/gcc-5.3.0 /usr/local mkdir -p /usr/local/share, cp -sfrv /usr/local/prog/gcc-5.3.0/bin /usr/local/. cp -sfrv /usr/local/prog/gcc-5.3.0/include /usr/local/. cp -sfrv /usr/local/prog/gcc-5.3.0/lib /usr/local/. cp -sfrv /usr/local/prog/gcc-5.3.0/share/man /usr/local/share/.
To execute the commands, pipe to sh. Then convert absolute links to relative links:
#!/bin/bash -e p("cd /usr/local"), # # -r: Recursive # -c: Convert absolute links to relative links # -d: Remove dangling links # -s: Detect lengthy links # symlinks -rcds bin etc include info lib man share \> /dev/null})
Call this script clean-usr-local.sh. To uninstall:
$ rm -rf /usr/local/prog/gcc-5.3.0 $ clean-usr-local.sh
That is all that is needed to get full control of /usr/local, but to go one step further, I have a script that:
- Deletes the “target” subdirectories of /usr/local, like bin and share.
- Reinstalls the symbolic links for the packages I want.
- Performs additional installation, like updating /etc/ld.so.conf.d/.
Warning! Be really, really careful when experimenting with things like this. A single bad sudo rm -r can completely destroy your file system. That is why I often print commands to stdout, instead of executing them. You have been warned.
You can reach me by email at “lars dash 7 dot sdu dot se” or by telephone +46 705 189090