Ask HN: Why do Makefiles typically not include an uninstall script?

5 points by codebeaker ↗ HN
I'm typically a package manager guy, once you start screwing with the status-quo especially on distributions like Ubuntu, things get strange really quickly.

Case in point, Redis, the Makefile is so simple, and the code/project so clean and with so few dependencies, there's not even a `./configure` stage, why doesn't it include a `make uninstall`.

`make install` must generate a file list to perform the uninstallation, it should be trivial to uninstall those files.

Am I missing a trick, is there some way to snatch the file list from `install` and feed it to `rm`, or do they not include an `uninstall` task incase that try to uninstall something that's running (but, even that is a small portion of things which are installed, and surely 9/10 times it would keep running until stopped/rebooted??)

Thanks HN!

9 comments

[ 3.2 ms ] story [ 27.1 ms ] thread
Debian has checkinstall, maybe it'd work for you? (http://www.debian-administration.org/articles/147)
> checkinstall

Thats a half-decent idea, nice smart move, and it looks like the authors motivations weren't dissimilar to my starting point for this query.

I do wonder however if it isn't slightly over-killed, `make install` must generate a list of commands to run, or there should have been some way to stream the output of `make install` to a text file, that it might be undoable ?

The clean way to use projects like Redis is to unpack them into an "/opt" directory (either in your home directory or the root file system). Then just `make` without the install and add that directory to your system path. In that case of Redis, you have to add the Redis `src` subdirectory to the system path in order to be able to use the `redis-server` command. You probably know all this already, but it's really the easiest way to keep track of projects you build yourself.

I think there is no `uninstall` command included usually because most packages that are installed using `make install` are system libraries that other packages depend upon. And so just uninstalling that one package could result in a broken system. There may be some other historical reason, but that's my guess.

EDIT: Yes, `checkinstall` is a really cool idea, but I've had a hard time getting it to work reliably in practice. Building a deb is a complicated process, so it's not surprising that attempting to automatic that process is not 100% reliable. But you should try it out, maybe you'll have better luck that I did.

I highly recommend using stow/xstow[1]. Pick a place to "stow" your "out-of-band" programs, then:

  cd <awsome-src-distribution>
  ./configure --prefix=/usr/local/ && make
  make install prefix=/usr/local/opt/<awsome-1.2.3>
  cd /usr/local/opt
  xstow <awsome-1.2.3>
(aptitude install xstow on Debian & Ubuntu)

Xstow is also small enough to fit it into eg: ${HOME}/opt -- and it is able to "unstow".

[1] http://xstow.sourceforge.net/

edit: formatting

To clarify; what (x) stow does, is manage a set of symlinks from/in eg: /usr or /usr/local to a similar tree under eg: /usr/local/<program-version/, so that for instance:

/usr/local/bin/<program> -> /usr/local/<program-version>/bin/<program>

Similarly for man, lib etc. If you don't put stuff under /usr or /usr/local (but, say under /opt or ${HOME}/opt) -- you need to update your paths (PATH, MANPATH, USER_LD..).

Uninstalling just means removing the symlinks (unstow) and deleting the tree under /wherever/<program-version>.

This is what I do in my bashrc (which I use across a range of systems, some of which are mine, some of which I only have a user accounts -- some on Solaris, most on a flavour of Linux):

  # set PATH so it includes user's private bin if it exists
  for dir in "${HOME}" "${HOME}/opt" "${HOME}/opt/${unps}"
  do
    if [ -d "${dir}/bin" ]
    then
      PATH="$dir/bin":"${PATH}"
    fi

    # For python binaries, in "default" virtenv:
    if [ -d "${dir}/pybin" ]
    then
	PATH="$dir/pybin":"${PATH}"
    fi

    # do the same with MANPATH
    if [ -d "${dir}/man" ]
    then
	MANPATH="${dir}/man":"${MANPATH}"
    fi

    if [ -d "${dir}/lib" ]
    then
      USER_LD_LIBRARY_PATH="${dir}/lib":"${USER_LD_LIBRARY_PATH}"
    fi

    if [ -d "${dir}/include" ]
    then
      USER_LD_RUN_PATH="${dir}/include":"${USER_LD_RUN_PATH}"
    fi

  done

  #Then later:
  export PATH MANPATH USER_LD_LIBRARY_PATH USER_LD_RUN_PATH

The part about "pybin" is just becaus I do:

  cd ~/opt
  mkdir python-venvs
  virtualenv --no-site-packages python-venvs/misc
  ln -s python-venvs/misc/bin pybin
And can then pip install stuff and have it available without having to worry about activating a virtualenv etc. I generally keep large packages in separate venvs -- but install stuff like mercurial in the "misc" one.

Note that the structure of my opt-folder mirrors that of eg. /usr/local, so:

  mkdir -p ~/opt/{bin,include,lib,man,share}
(I also have src and xstow in there -- src for source packages I want to keep around, xstow for xstow-installed packages.)

And to repeat, if you put your xstow tree under /usr/local or somewhere most likely already in your path, it should "just work" without having to set any extra variables -- and is more useful for eg: installing a system wide service (like Redis for production use).

edit: forgot to add export of environment variables to the .bashrc snippet

edit: the "${HOME}/opt/${unps}" points to a directory that contains architecture specific binaries; the unps-variable, is set to be `uname -ps` (with some trickery) -- and is then used for finding local binaries and setting some options like PAGER.

Stow seems to have a flavor of Mac homebrew about it (more likely the other way around) everything is compiled and installed into `/Library/Cellar` and then symlinked into the system path, you can `brew unlink readline`, for example and it goes away, link it back and it's available to the system again.

The `brew` executable knows about the correct flags to give to `--configure` within it's own context, so it rarely goes wrong.

> I think there is no `uninstall` command included usually because most packages that are installed using `make install` are system libraries that other packages depend upon. And so just uninstalling that one package could result in a broken system. There may be some other historical reason, but that's my guess.

That's a good point, there is no dependency resolution at this low-level; and with that in mind, there's probably a good way to uninstall things by hand, if you really wanted to.. thanks for the thought.

As for answering the first question, maybe this might be a hint:

http://sources.redhat.com/autobook/autobook/autobook_109.htm...

  "In our experience, uninstall is not a very useful feature. Automake implements it because it is mandated by the GNU Standards, but it doesn't work reliably across packages. Maintainers who write install hooks typically neglect to write uninstall hooks. Also, since it can't reliably uninstall a previously installed version of a package, it isn't useful for what most people would want to use it for anyway. We recommend using a real packaging system, several of which are freely available. In particular, GNU Stow, RPM, and the Debian packaging system seem like good choices."
Note this refers to GNU Stow[1] which is implemented in perl -- I much prefer the xstow variant[2] as it is more self contained. YMMV.

[1] http://www.gnu.org/software/stow/ [2] http://xstow.sourceforge.net/