Exercise 5: Patching, config, pre-/postinstall/-rm and subversion

[Warning]Warning

This exercise is a jump up in complexity, so it gets harder to give precise copy-and-paste instructions. It is therefore time to stop sticking closely to the example and start trying to feel your own way through the thicket, still loosely guided by the steps in the exercise.

The previous exercise still wasn't very realistic. GNU Hello is very well-behaved, but in practice, software makers put their files in the wrong directories, with the wrong permissions. They omit manpages, link against libraries they don't use, hardcode paths and other settings, and generally generate bugs, the list goes on and on. So in practice, software needs to be patched before it can be packaged. Of course you offer your patches to upstream, so patching should be done in nice discrete blocks, so that when upstream copies one of your patches - or fixes a bug in another way -, you don't need to wade through complex multipatches to remove the part that isn't needed any more. To make matters still worse, while you are packaging the new version of some software, a bug may be solved in an older version, and you may need to patch and repackage that.

Figure 2.4. Packaging with svn-buildpackage

A diagram showing processes and files involved in creating patched packages with svn-buildpackage

So this time, we're going to patch the software we package, and we're going to bring our packaging effort under version control, then generate source and binary packages straight from the VCS.

Procedure 2.4.  Patching packages

  1. Fetching the package

    For this exercise, we are going to work with scaramanga, which I scripted and actually use for mirroring Ubuntu, as well as a couple of other distros. It's not very sophisticated, which is perfect for the purpose of this exercise. And as it is a script, it doesn't need compiling.


          
    cd ~/packaging
    mkdir scaramanga
    cd scaramanga
    wget http://www.cs.rug.nl/~jurjen/scaramanga-0.0.4.tgz
    tar zxvf scaramanga-0.0.4.tgz
          

        

  2. Initial Debianization


          
    cd scaramanga-0.0.4
    dh_make --copyright gpl -f ../scaramanga-0.0.4.tgz --single
          

        

    [Note]Note

    Please verify that trying to build the package fails

  3. Preparing for scripts instead of compiled programs

    1. Modifying debian/rules

      Since packaging doesn't this time involve running configure, make and make install, and installation of files is just a matter of copying, we can simplify debian/rules:

      #!/usr/bin/make -f
      # -*- makefile -*-
      # Sample debian/rules that uses debhelper.
      # This file was originally written by Joey Hess and Craig Small.
      # As a special exception, when this file is copied by dh-make into a
      # dh-make output file, you may use that output file without restriction.
      # This special exception was added by Craig Small in version 0.37 of dh-make.
      
      # Uncomment this to turn on verbose mode.
      #export DH_VERBOSE=1
      
      build: 1
      
      clean:
              dh_testdir
              dh_testroot
              rm -f build-stamp configure-stamp
      
              # Add here commands to clean up after the build process.
              $(MAKE) clean
      
              dh_clean
      
      install: 2
              dh_testdir
              dh_testroot
              dh_clean -k
              dh_installdirs
      
              # Add here commands to install the package into debian/scaramanga.
              $(MAKE) DESTDIR=$(CURDIR)/debian/scaramanga install
      
      3
      
      # Build architecture-independent files here.
      binary-indep: install
              dh_testdir 4
              dh_testroot
              dh_installchangelogs
              dh_installdocs
              dh_installexamples
      #       dh_install
      #       dh_installmenu
      #       dh_installdebconf
      #       dh_installlogrotate
      #       dh_installemacsen
      #       dh_installpam
      #       dh_installmime
      #       dh_python
      #       dh_installinit
      #       dh_installcron
      #       dh_installinfo
              dh_installman
              dh_link
              dh_strip
              dh_compress
              dh_fixperms
      #       dh_perl
      #       dh_makeshlibs
              dh_installdeb
              dh_shlibdeps
              dh_gencontrol
              dh_md5sums
              dh_builddeb
      
      # We have nothing to do by default.
      
      binary: binary-indep
      .PHONY: clean binary-indep binary install
      		

      1

      configure(-stamp) has been removed, and build exists but does nothing

      2

      install depends on nothing any more

      3

      binary-arch not neede so removed

      4

      All the debhelper commands have been moved from under binary-arch to binary-indep.

    2. Changing the target architecture in debian/control

      Disregarding the rest of debian/control, we can already set the line:

      		  Architecture: all
      		

      instead of

      		  Architecture: any
      		

      In the any case, the package building software will try to build packages for any architecture you ask from the source. In the all case, only one binary package will be built, which should work on any architecture. (Am I the only one who finds the terms and meanings counterintuitive?)

      [Note]Note

      Please verify that running debuild -rfakeroot still fails, and that this is because the Makefile doesn't respect the $DESTDIR environment variable.

  4. Patching

    Now that make install fails, we can basically do two things. We can either fix the Makefile, or we can fix debian/rules. We do the latter, using dpatch.

    1. Adjusting debian/rules

      #!/usr/bin/make -f
      # -*- makefile -*-
      
      <snip>
      
      clean: clean-patched unpatch 1
      
      unpatch:
              dpatch deapply-all
              rm -rf patch-stamp debian/patched
      
      clean-patched:
              dh_testdir
              dh_testroot
              dh_prep
              ${MAKE} clean
              rm -rf debian/scaramanga debian/files debian/substvars
      
      install: patch-stamp 2
              dh_testdir
      
      <snip>
      
      binary: binary-indep
      
      patch: patch-stamp
      
      patch-stamp:
              dpatch apply-all
              dpatch cat-all >patch-stamp
      
      .PHONY: build clean unpatch clean-patched binary-indep binary install patch
      		

      1

      Clean before unpatching. This makes sense: reverting patches in a directory that contains files generated by the build process may easily fail.

      2

      Patch before installing. In case of a compiled program. patching will come before configure, but since in our case install was the earliest step, we put it before that.

    2. Creating patches

      That drops us into a shell, in which we edit the Makefile so that it does install into $DESTDIR if that variable is set.

      [Note]Note

      Please do so now, and also consider whether that clean target is suitable at all.

      When done editing, we exit 0 from the shell, and the patch is created for us in debian/patches.

      [Warning]Warning

      Please note that it is not a good idea to run dpkg-buildpackage in the spatch-edit-patch shell. If we were to do that, all changes make to the directory made by that program would end up in the patch.

    3. Listing the patches to be applied

      The patch we just created won't be applied during packaging inless we list it in debian/patches/00list:

      [Note]Note

      To see whether the patch is indeed applied, run debuild -rfakeroot and see that it runs dpkg-buildpackage with success (of course the lintian part generates a lot of errors and warnings).

  5. Editing the control files

    [Note]Note

    By now you should be able to remove the clutter from ./debian and edit the appropriate files until lintian stops complaining. Please do so. Fix the script permission problem using a dpatch patch that depends on the 010-Makefile-respect-DESTDIR one (see the manpage).

    [Warning]Warning

    Be aware that dpatch supports interdependent patching in the sense that you can create a patch B that requires that patch A has already been applied, but that it is up to you to keep track of the order in which to apply the patches, in debian/patches/00list.

  6. Bringing the packaging under version control

    Now that we've got a ../datepipe_0.1.0-1.dsc, we can use the svn-buildpackage suite to bring and keep it under version control:

    [Note]Note

    Just to keep us in sync: I am now working in ~/packaging/scaramanga/scaramanga-svn/scaramanga

  7. Building from versioned packaging

    [Note]Note

    The --svn-ignore defies logic a bit. It is meant to make the build process continue even if SubVersion is constantly nagging you about uncommitted files. That makes the packaging process a lot easier, but it does mean that it is your duty to every now and then remove that option and save an actual copy to the repository.

  8. Trying out the package

    In ../build-area you can see the various files that svn-buildpackage has delivered. Install the binary package on a machine where you have root, and run

  9. Adding a user during install

    Scaramanga refuses to run because it expects to run with the UID of a dedicated user. The name of that user can be configured in /etc/scaramanga, but an elegant solution would be to add a user scaramanga when the package is installed, and remove it when the package is removed. This is one of the things that can be done in {pre,post}{install,rm} scripts. To be precise, debian/preinstall and debian/postrm are the scripts to add.

    Because we removed the {preinstall,postrm}.ex in a previous step, and because our working directory now has a name that dh_make doesn't understand, we cannot use dh_make -a to give us back our examples. But we can copy the examples straight out of /usr/share/debhelper/dh_make/debian/.

    For convenience, I will give example here of preinst:

    #!/bin/sh
    # preinst script for scaramanga
    #
    # see: dh_installdeb(1)
    
    set -e
    
    # summary of how this script can be called:
    #        * <new-preinst> `install'
    #        * <new-preinst> `install' <old-version>
    #        * <new-preinst> `upgrade' <old-version>
    #        * <old-preinst> `abort-upgrade' <new-version>
    # for details, see http://www.debian.org/doc/debian-policy/ or
    # the debian-policy package
    
    
    case "$1" in
        install|upgrade)
    
            addgroup --system scaramanga
            adduser --system --no-create-home --ingroup scaramanga --disabled-password --disabled-login --gecos 'mirrorer' scaramanga
    	mkdir /srv/mirror
    
        ;;
    
        abort-upgrade)
    
        ;;
    
        *)
            echo "preinst called with unknown argument \`$1'" >&2
            exit 1
        ;;
    esac
    
    # dh_installdeb will replace this with shell code automatically
    # generated by other debhelper scripts.
    
    #DEBHELPER#
    
    exit 0
    	    

  10. Providing sensible defaults

    [Note]Note

    Now that our debian/preinstall creates /srv/mirror, we should create our mirrors in that directory too, so we need to supply a sensible /etc/scaramanga/scaramanga.conf to be installed. This can be done in debian/rules or in a patch. Take your pick and while you 're at it, please remove superfluous comments as well.

  11. Committing changes to version control

    It is time to commit our changes:

    [Note]Note

    Notice that svn-buildpackage without the --svn-ignore option still fails because debian/scaramanga.debhelper.log gets modified on pre-install clean.

    An editor pops up (if you set $EDITOR) and you enter:

    	      *.debhelper.log
    	    

  12. Adding to the changelog

    Then we want to update debian/changelog. This is done through creating a new tag:

    Then editing debian/changelog:

    scaramanga (0.0.4-2) unstable; urgency=low
    
      * Added preinstall to create mirrordir and dedicated user, and provided sensible default config
    
     -- Jurjen Bokma <j.bokma@rug.nl>  Sun, 04 Oct 2009 21:07:21 +0200
    
    scaramanga (0.0.4-1) unstable; urgency=low
    
      * Initial release (Closes: #9999)
    
     -- Jurjen Bokma <j.bokma@rug.nl>  Sat, 03 Oct 2009 20:57:15 +0200
    	    

    And re-building:

    [Note]Note

    Now rebuild the package using svn-buildpackage with the appropriate options, upload it to the target system, rebuild the repository indices, install the package, and see whether it works.

    [Warning]Warning

    Please be advised that if you had omitted updating the changelog and hadn't used a simple local repository that rebuilds its entire index with each upload, but a more sophisticated one like reprepro, then you would have gotten errors upon upload. But you would have been able to get back the old version from the subversion repository.

    [Note]Note

    Note that sudo -u scaramanga /usr/bin/scaramanga -d debian still fails because the default directory to mirror to doesn't exist. Fix that and rebuild.