How to safely log self-compiled applications

Occasionally you may wish to self compile an application, because it is not found in your distro's repositories, you want different options or patches compiled in, or to install a more recent version. The best course of action is to make a native package to ease future removal or upgrade. However, depending on your distro this can be time consuming and/or convoluted. Below I provide a fairly simple alternative.

What about "checkinstall" or "make uninstall"?

There are established tools to assist with automatic package creation and/or logging (checkinstall, porg, etc.) but they are not without issues. They typically use techniques that attempt to track the installation (using LD_PRELOAD to intercept system calls or find commands). Tracking however is often imperfect and does not prevent the overwriting of files. You also don't always have the option of adjusting the package contents before install (adding, moving or removing files, adjusting faulty permissions, compressing man pages, striping binaries, etc.).

Another possibility is make uninstall. This suffers all the problems of tracking solutions and some new ones. Very often there is no uninstall target or it is poorly tested and hence buggy, removing too little (or worse too much). You also have to keep the source directory around.

The alternative

The following is yet another alternative. It takes the core idea of traditional packaging (a "staging" directory) and pairs it with a simple to create, uninstall script.

Like with traditional packaging, you first install into a temporary directory. From here, you can check where files will be installed (to avoid overwriting already existing ones), then adjust and/or remove them (or perhaps add more). You can also strip binaries, compress man pages and tweak file permissions as you see fit. Once done, you then create a very simple uninstall script and then transfer the package contents into their final install location.

To understand the process, skip ahead to Install to a temporary, "staging" directory or if you just want something a little more automatic, read on.

Automation

For something more automated, try the shell script transfer-staged-package.sh. Simply use it during the Tweak the package contents step below. It will create a fancier uninstall script and also strip any binaries, compress man pages, fix ownership and permissions and then transfer the package to the final install location, by issuing one command:

transfer-staged-package.sh packagename  

Note: This script has several options. You can disable man page compression, binary stripping and permission and ownership changes. You can also create a tar archive instead of installing (for backup or easily transferring your application to another machine).transfer-staged-package.sh --help lists the options.


If you prefer do things manually, or to understand the essence of how that script works, here are the basic steps.

Install to a temporary, "staging" directory

Use DESTDIR=, INSTALL_ROOT=, --prefix= or similar to adjust the directory where initial install takes place. This also has the benefit of allowing you to perform this risky install step as a normal user.

./configure
make  
make install DESTDIR="$PWD/staging"  

Switch into the staging directory

cd staging  

Tweak the package contents

Within this staging directory, you can now move files around to avoid clashes with pre-existing files (if needed), strip binaries, compress man pages, adjust permissions, etc. or make any other adjustments as you see fit. At the very least run a find command to see the contents of the package:

find .  

Make an uninstall script

Adjust packagename in the code below, to be the name of the application you are installing and then run the rest of the commands verbatim to create a basic uninstall script.

Note: Only files (and symlinks) are recorded in the uninstall script (not directories). This is because directories are often shared between applications.

Fix ownership and permissions

Issue the following commands as root or prefaced with sudo:

chown -R root:root .  
chmod -R u+w,go+r-w,a+X-s .  

Note: If you have been using your normal user account up to this point, all files will be owned by you, rather than root. So unless you have good reasons not too, the example chown command above is recommended as a minimum.

Install the application globally

Issue the following as root or preface the second tar command with sudo:

find . ! -type d | tar cf - -T- | tar xvf - -C/  

The application is now installed and can be run.

Note: Only files are transferred. This avoids updating or adjusting permissions and timestamps on shared directories. New directories will be created as needed with default permissions.


Uninstalling a package

If you need to uninstall in the future, issue the following as root or prefaced with sudo:

/usr/local/bin/uninstall-packagename

Note: While all files will be removed, any non-shared (application specific) directories will be left behind. However, they should not cause any issues, nor take up any significant amount of space. If the idea of empty directories bother you, you can find them with empty-dirs-usr-local.sh and manually remove them or use the transfer-staged-package.sh shell script in the future, since it makes a fancier uninstall script to handle these.


Tip and tricks

Here are a few extra tips, to work with packages installed this way.

Note: For each of these, remember to adjust packagename to be the name of the application.

Tracking installs

Occasionally, software might not provide an install method that can be easily redirected to a staging directory. In this case, you can use find to work out what files where installed and use this to generate the uninstall script.

After installing the software, issue the following (as root of prefaced with sudo) to get a list of files (and symlinks) that have been created in the typical install locations, within the last five minutes:

find /etc /opt /usr -cmin -5 ! -type d  

If this seems to accurately represent the files from the package you installed, switch to a root shell (sudo -s on distros which don't generally use root directly) and proceed to make an uninstall script as follows:

Review and edit (as needed) the uninstall script you just created. Once complete, remember to exit the root shell.

Note: The above is just an example find command with the most common directories software is likely to install into. You may wish to adjust the directory list and timing slightly if you feel that something is missing. Additionally, if some files are listed that you do not believe should be there, edit the uninstall script to remove them.

Repackaging

You can also use this technique to repackage applications, which could be handy if the package is in the wrong format (a deb but you use an rpm distro) of if you want to remove dependencies. Often a program will partially work (or one of the included binaries will run) with a lower set of dependencies. If you only need the limited functionality, this can be a way to avoid bloating your system.

Extract the contents of the package into the staging directory, move everything in usr to usr/local and follow the rest of the steps to create an uninstall script before transferral everything to the respective global locations.

Checking files owned by a package

A list of files associated with the package can be printed as follows:

grep ^/ /usr/local/bin/uninstall-packagename  

Creating a backup archive

If you want to backup the package (also handy for transfer to another machine).

grep ^/ /usr/local/bin/uninstall-packagename |\  
  tar cPJf packagename-bin.tar.xz -T-

To install from this backup package, issue the following as root or preface it with sudo:

tar xPf packagename-bin.tar.xz  
comments powered by Disqus