Introducing Fetchware: package management for source code distributions.
Years ago when I was a linux noob, I loved to install software from source code instead of lame vendor packages. It was cool. And it followed naturally from using Slackware as my linux distro of choice as Slackware still has more of an old school Unix feel to it instead of easy to use Ubuntu lameness. It was also encouraged by Slackware, which probably has the smallest repository of all linux distros, because it is still limited to what fits on 6 CDs or 1 DVD, but there are Slackbuilds for extra packages. What I loved most was getting to choose how the program was configured. Figuring it out was usually a simple ./configure --help away.
What I hated were monthly updates due to security holes, because then I'd have to download, compile, and reinstall it manually again. Once was fine, but every few weeks or months was annoying. So, I quickly came up with a cool idea to write a simple perl script to automate this. This simple script has been written and rewritten four times now, and finally works well. I called it fetchware, because it was the best semi-descriptive name I could come up with.
Fetchware is a package manager for source code distributions. If configured appropriately, it can automatically upgrade (by recompiling and reinstalling) any source code distribution. It currently uses its own simple package format and package database format, and currently can not create native packages.
It can also install, upgrade, upgrade-all (upgrade all installed packages), list (list installed packages), and even uninstall packages (But only if the source code distribution's build scripts provide a way to do this such as a 'make uninstall' make target.) I also added the traditional CPAN command's look command to fetchware for looking at modules without building or installing them.
Fetchware also comes with a really cool new command that allows you to create new Fetchwarefiles (what fetchware calls its configuration file) via a simple question and answer interface. These Fetchwarefiles contain fetchware's configuration options:
use App::Fetchware;
program 'Apache';
lookup_url 'http://www.apache.org/dist/httpd/';
filter 'httpd-2.2';
mirror 'http://apache.mirrors.pair.com/httpd/';
mirror 'http://mirrors.ibiblio.org/apache/httpd/';
mirror 'ftp://apache.cs.utah.edu/apache.org/httpd/';
verify_method 'gpg';
gpg_keys_url 'http://www.apache.org/dist/httpd/KEYS';
make_options '-j 4';
prefix '/home/dly/software/apache2.2';
# You can use q{} to make gigantic options like this one more legible.
configure_options q{
--with-mpm=prefork
--enable-modules="access alias auth autoindex cgi logio log_config status vhost_alias userdir rewrite ssl"
--enable-so
};
The syntax might seem familiar, because Fetchwarefiles are Perl. Fetchware
uses prototypes to make Perl look like some sort of configuration file language.
You'll also notice the use App::Fetchware; line, which is where the configuration subroutines such as program, lookup_url, and filter come from. The reason they are Perl instead of using .ini format like dzil does or some other format such as the original Apache format I used in fetchware's predecessor is because source code distributions vary wildly in how they work, so some custom Perl code may be needed here or there to make fetchware work with each and every source code distribution you find. See the example Fetchwarefiles section in fetchware's documentation for additional examples.
Fetchware also supports fetchware extensions that allow you to change all or
just part of fetchware's behavior, and even add your own custom configuration options such as the ones Fetchware uses like program or lookup_url. Currently only an example one exists called App::FetchwareX::HTMLPageSync, which I wrote to automate downloading wallpaper; however, I intend to write a real one that will
actually be usefull.
Since Fetchwarefile's are Perl, and the use App::Fetchware is mandatory, you may
be wondering exactly how fetchware's Fetchwarefiles work. It takes advantage of
eval. Eval evals the Fetchwarefile inside of fetchware, so any modules use()'d in the Fetchwarefile will also be available to the larger program. What that means is the "luse App::Fetchware;" imports the configuration subroutines into the Fetchwarefile making the configuration options/subroutines work. But it also imports what fetchware calls its "API subroutines," which are start(), lookup(), download(), verify(), unarchive(), build(), install(), and uninstall(). These imported subroutines are then called later on in the actual fetchware program one by one in order to actually install whatever program you configured fetchware to do.
This design came from past failed attempts at creating fetchware where I wasted all of my time creating various complicated configuration file parsers instead of actually writing code to download and install stuff. So, this time around I ditched any attempt at actually creating a parser. First, I was inspired by mytop's configuration file, which just uses require to abuse Perl into parsing the configuration file for it. This caused me to remember that Makefile.PL looks like a hash of config options, and then it just calls a function to make the Makefile. Thinking of how Makefile.PL works
reminded me of how Moose adds its "new syntax" to perl by just abusing prototypes to get rid of parenthesis. This all inspired me to just abuse Perl and prototypes and eval to have a usable configuration file syntax that could believably be a configuration file instead of look too much like a programming language like mytop or Makefile.PL do. So, that's how I came up with this crazy design if you were wondering how I could come up with something this crazy.
This is Fetchware's initial release, so you may run into issues using it. It also currently only has FAIL reports on CPANTesters :) But these fail reports seem to mostly just be silly mistakes I've made, so you may have test failures to look into if you're interested. I'm currently working on fixing them as I go, but CPANTesters is currently down as I post this. Also, Windows is not currently supported or tested, so if you're running Windows it will most likely fail. I have used File::Spec or Path::Class to deal with path names universally, so it should not take too much to add Windows support. I just have yet to add it and test it.
Fetchware is available on CPAN now if you would like to try it out. And its code is on github (but on github you'll need dzil to build it).
Very cool!