#!/usr/bin/perl # -*- mode: cperl; coding: utf-8; -*- use strict; use warnings; use utf8; use lib "/h/hamren/src/post/lib", "."; my $rval = do "common.pm" || die "$0: common.pm failed ($!) [$@]"; #--- Single-line common initializer #--- End of header post( header(), p("When downloading, compiling and installing applications from source, the installed programs ususally end up in ", tt("/usr/local/bin"), ", ", tt("/usr/local/share"), ", ", tt("/usr/local/lib"), " and other subdirectories of ", tt("/usr/local"), ". After a while ", tt("/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. ", tt("make uninstall"), "), but not all."), p("There is a simple way of solving this problem. Instead of installing all packages to ", tt("/usr/local"), ", install each package to its own subdirectory of ", tt("/usr/local"), ", then symlink it to ", tt("/usr/local/bin"), " &c. A package can then be removed by a) removing its own unique subdirectory, and b) removing all dangling symbolic links."), p("If the source is in ", tt("/usr/local/src/gcc-5.3.0"), ", then put the resulting program in ", tt("/usr/local/prog/gcc-5.3.0"), ". This example assumes GNU configure:"), source_codeq(<<'EOF'), $ cd /usr/local/src/gcc-5.3.0 $ ./configure --prefix=$(/bin/pwd | sed -e 's/src/prog/') $ make && make install EOF p("Then install into the regular places using symbolic links. GNU ", tt("cp"), " has an option ", tt("-s"), " for doing just that. Here is a script to automate installation:"), source_codeq(<<'END'), #!/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 END p("The odd-looking assignment ", tt(q), " extracts the very last argument on the command line. The even odder-looking for statement ", tt(q), " loops over all but the last argument. This makes the script behave as ", tt("cp"), " and ", tt("mv"), ", which take one or more source arguments, and a target argument at the end."), p("The script writes command to stdout."), source_codeq(<<'END'), 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/. END p("To execute the commands, pipe to ", tt("sh"), ". Then convert absolute links to relative links:"), source_codeq(<<'END'), #!/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}) END p("Call this script ", tt("clean-usr-local.sh"), ". To uninstall:"), source_codeq(<<'END'), $ rm -rf /usr/local/prog/gcc-5.3.0 $ clean-usr-local.sh END p("That is all that is needed to get full control of /usr/local, but to go one step further, I have a script that:"), ul( q . tt("/usr/local") . ", like " . tt("bin") . " and " . tt("share") . ".", "Reinstalls the symbolic links for the packages I want.", "Performs additional installation, like updating " . tt("/etc/ld.so.conf.d/") . "."), p("Warning! Be really, really careful when experimenting with things like this. A single bad ", tt("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."), footer() );