Autopackage architecture documentation

This document describes in an overall fashion how autopackage works, and how the bits and pieces fit together. It's meant to be a good introduction for those who are curious, or for people who want to hack on the code itself. Unfortunately it's something of a dry read, as it's a technical document: perhaps somebody could draw some pretty pictures for it?

Autopackage is a system that lets you create powerful and flexible installer packages (these have a .package extension) that can install their payload onto any vaguely Linux related distribution, check for and retrieve dependancies, verify and autorepair itself if/when it gets broken and finally perform the uninstall. Autopackages do not require all your other software to be installed using it - and therefore they will work on all distros regardless of whether they use RPM, DPKG, emerge or whatever. Instead, when a package depends on the presence of another piece of software, it directly checks the system for its presence and goes to download the necessary package if the check fails. This means you can install software from the source, or even copy it from another computer and autopackage will still be aware of its presence.

autopackage is made up of 3 fundamental pieces:


Life of an autopackage: installation

An autopackage starts life, like all packages, as a source tree with a specfile. To be more specific, the specfile is called autopackage.spec and is structured like an INI file, with sections denoted by angled brackets. The specfile contains quite a bit of information, including:

The root name is an important concept in autopackage. Root names provide an answer for how to uniquely identify a package in a scalable fashion, rather than the ad-hoc system currently used in most distributions. A root name looks like @xchat.org/xchat/1.0. Broadly speaking, it's an opaque string that should nonetheless follow certain rules. It should start with an @ symbol, be followed by the URI of the packages netsite (if it doesn't have one, a simple description page can be placed on autopackage.net which will do), followed by a slash then the version. Any other information can be embedded into the root name as long as it is in the form of a path.

Once written, an autopackage.spec file is processed with the makeinstaller tool. This program will read the specfile it finds in the directory passed as the first parameter, and proceed to construct the package file. The first thing it does is run the Prepare section of the specfile (which is just a bash script). This can do anything you like, however it must result in an "installation" of the compiled program files into /tmp/apkg-build-root. If you're using the GNU Autotools framework, you can simply use the built-in prepareBuild function which will run configure, make, make install for you (as well as providing progress feedback). The extractions section is then run. Again, it's just a bash script, and it's job is to move all necessary files into $workingdir. Once more, this is automated via a built-in function: extract. extract takes three parameters. The first is the location inside the package where the files will be placed. Normally this is pretty easy to provide a value for - it doesn't really matter where you put things as they'll all be rearranged when the package is installed anyway. The second is where to extract the files from, and will almost always be either "$build_root" - which is where the program was built to (by default /tmp/apkg-build-root), or "$payload_dir" which is the directory in which the specfile resides (ie the source tree).

Once the necessary files are extracted from the build root and the payload directory, some parts of the specfile are copied into the working directory as well. In particular, the install and uninstall scripts are important. However, the bit we're interested in is the skeleton files.

A skeleton file is a special type of text file, again structured like an INI file, which describes a package. It is similar to a specfile in many ways: for instance it contains metadata. However, a skeleton file has some new sections and leaves out some others compared to a specfile. The most important section is [Test] which contains a script unique to each package. The purpose of this script is to probe the users system for the presence of the software that the skeleton file represents. It can range from very simple such as checking for the presence of a library using the built-in testForLib, to the quite complex such as figuring out the version of a command line program based on what error messages it provides :) The Test script is like a function, and should return its results using either "return $TEST_PASS" or "return $TEST_FAIL". If the test fails, then it's assumed the package needs to be downloaded and installed so the [Retrievals] section is run. 99% of the time this will just hand over to the retrieve built-in, which talks to the autopackage network and automates the process of downloading and installing the dependancy package.

Skeleton files need to be produced for any dependancies you may need, and then placed into a special directory. When producing the .package file, makeinstaller will scan the install script for the "require" built-in, and include any skeleton files needed. The require built-in is passed a root name, and then carries out the necessary functionality (checking, downloading and installing if necessary). That's basically all the process boils down to - one call to require. Easy, isn't it?

Anyway. Needless to say, there's a lot more to installing software than just checking for dependancies. Next up is user interaction (possibly) in which we ask the user any relevant questions. Because autopackage has multiple front ends, some of which may be very different, this process is not quite as intuitive as you might think. It basically consists of calls to the select methods, and then a call to waitForSelections. This will pause the script until the user has answered all the questions and is necessary because if for instance a graphical installer frontend is being used, the questions will not be linear as the user can jump around between the controls, change their mind and so on. The waitForSelections method will just hang around until all the answers are available. For some front ends that query the user as the select methods are called (for instance the terminal front end) this function doesn't actually wait.

Once the questions have been answered by the user, you can get the results by calling the get method. One thing to bear in mind is that if in a network install scenario the answers may be recorded and then played back to the install script. Before you actually begin to copy files across, it's necessary to have a few environment variables set up: namely prefix. Finally, you can use the copyFiles method to actually transfer the files to the computer. At any point in this script you are free to do anything you want, such as running special setup scripts and so on.

Finally, after the install script has run, autopackage will create a logfile and place it into the packages database directory (by default /var/packages).

What next?

Next up is verification. People break things - this is life. It'd be better if they were prevented from breaking things, but all that would do is piss off people who know what they're doing, without actually stopping people breaking things (remember: users rarely pay attention to warnings). As such, autopackage has an auto-repair feature. If something "breaks" on a system, a program will stop working, and you can run the verify command on it. What this does is rerun all the dependancy checks of the package: if one fails, the package is redownloaded and installed. Hopefully, this will restore the missing/damaged file(s) and the program will start working again. Needless to say, if it works the user will find this dead impressive ;) (even though really it should not have broken in the first place). This process is fully automatic, and the package creator doesn't need to do anything for it to work.

Goodbyeeeeeee

Unfortunately, no matter how great your program is, at some point somebody will want to uninstall it. Users can be awkward can't they ;) Uninstalling is very easy - the [Script-Uninstall] section of the specfile specifies what to do. Usually, this is just a call to reverseLog which will load up the logfile created at install time and use that to delete any copies that were made to the computer.


Porting

This is all well and good of course, but the thing that makes autopackage useful is the fact that .package files can be installed on any Linux distribution. The way this works is simple: when files are copied to the users computer, the destination string is run through a remapper script which translates the paths to the correct locations dependant on your distribution. In future this may be expanded to perform other distro-specific functions, but for now most distros vary only in the location in which they put things so this is what is dealt with.

Porting autopackage to a new distro is simply a matter of creating a new remapper script. It's fairly self-explanatory, you can see the suse.remapper which I created for my system.

Front end independance

Linux is changing - it's becoming less the domain of developers and more the domain of end users. The demands of these two demographics is very different: developers tend to be happy with, even demand command line interfaces which are fast and efficient. End users tend to be happier with graphical installer apps similar to InstallShield - autopackage attempts to cater to them all. What's more, there are a variety of front ends even in these two categories. For instance, some people may wish for an ncurses based installer interface that can be used from the command line but is semi-graphical. Others may want an installer that integrates with KDE, or GNOME, or Enlightenment, or whatever other desktop environemnt they are using. Therefore, the backend must be separated from the frontend in a cleanly designed fashion.

The mechanism by which this currently works is simple: a UNIX named pipe is set up in the filing system and the front end is forked as a separate process. They then communicate through this pipe, using a simple protocol, which is currently documented in the apkg-ttyfe file.