| Using pbuilder to backport Debian packages | ||
|---|---|---|
| Prev | ||
It's possible to add a few hooks that allows a pbuilder environment to utilize distcc, making it possible to leverage existing compile farms. It is critical to note that there is currently no standard way for supporting concurrency when building Debian packages, so consider this more of a hack that may result in broken builds or packages.
First, add the following hook to /etc/pbuilder/hook.d as D10distcc.
#!/bin/sh # Let's plugin to distcc. cd /usr/local/bin ln -s ../../bin/distcc cc ln -s ../../bin/distcc c++ ln -s ../../bin/distcc gcc ln -s ../../bin/distcc g++ ln -s ../../bin/distcc i386-linux-gcc ln -s ../../bin/distcc i686-linux-gcc ln -s ../../bin/distcc i386-linux-g++ ln -s ../../bin/distcc i686-linux-g++ |
Above, we perform the classic distcc masquerade hack. The latter filenames for invoking gcc and g++ are architecture specific and will need to be changed to suit your needs.
Additionally, the defined PATH must have /usr/local/bin prepended for the masquerade hack to work. CONCURRENCY_LEVEL specifies the number of allowed concurrent jobs for make to use and should be set suitably for distcc. BAD_PKG is discussed later.
export PATH="/usr/local/sbin:/usr/local/bin:$PATH" EXTRAPACKAGES=distcc export DISTCC_HOSTS="localhost buildhost" export CONCURRENCY_LEVEL=10 export BAD_PKG="" |
Now, it's time for the real magic. Once the package is unpackaged, we'll replace seemingly innocent invocations of make with a call that includes the concurrency option. As there is not yet any standard way of doing this from within a Debian package, you have to experiment to see if a build explodes before you know if it is safe for any given package.
#!/bin/sh
# Skip packages that cannot be parallelized
for pkg in $BAD_PKG ; do
if [ "${pkg}" = $(ls -1 /tmp/buildd) ] ; then
exit 0
fi
done
if [ -z $CONCURRENCY_LEVEL ] ; then
CONCURRENCY_LEVEL=6
fi
cd /tmp/buildd/*/debian
# Terrible hack, may fail a lot
perl -i -pe \
"s/(\\(|{)MAKE(}|\\)) -C/\$1MAKE\$2 -j\$ENV{CONCURRENCY_LEVEL} -C/" \
rules
perl -i -pe \
"s/(\\s+)make$/\$1make -j\$ENV{CONCURRENCY_LEVEL}/" rules
|
The hook above, which runs after the build dependency condition is satisfied, must begin with the letter A. For example, A10makehack in /etc/pbuilder/hook.d may be appropriate. In the packages I tested against, I found occurrences of make and {MAKE} -C candidates for abuse. Once you've discovered a package that cannot sustain being parallelized, you can add it to the BAD_PKG variable using its source package base name to skip it in the future.
ccache is a robust tool for caching compiled objects. A package can be substantially faster during subsequent builds once it has been compiled once. While it may appear that many packages won't need to be backported again, when larger packages fail and troubleshooting is required, it is far faster to recover if duplicate compiling effort isn't required. Additionally, you can plug ccache into distcc to leverage your existing compile farm to bootstrap your initial ccache caching store.
Configuring and installing ccache requires us to make a few changes to our /etc/pbuilder/pbuilderrc configuration.
EXTRAPACKAGES="ccache" export CCACHE_DIR=/var/cache/pbuilder/ccache export CCACHE_UMASK=002 BUILDUSERNAME=pbuilder BUILDUSERID=$(grep $BUILDUSERNAME /etc/passwd | cut -d: -f3) BINDMOUNTS="$BINDMOUNTS $CCACHE_DIR" export PATH="/usr/lib/ccache:$PATH" |
If you decide you want to use distcc you will want to include CCACHE_PREFIX.
export CCACHE_PREFIX=distcc |
Finally, let's add a new user and group to the host system. The presence of a user and group are necessary so ccache has permission to write to the actual directory that is bindmounted into the chroot environment. By default pbuilder will use a UID of 1234 which won't have write access to /var/cache/pbuilder/ccache. We don't want to recycle an existing user or something inside the chroot could effect processes that may be running as that user.
# addgroup --system ccache # adduser --quiet --system --ingroup ccache \ --home /var/cache/pbuilder --no-create-home pbuilder # mkdir /var/cache/pbuilder/ccache # chown pbuilder:ccache /var/cache/pbuilder/ccache # chmod g+ws /var/cache/pbuilder/ccache |
Above, we add a new group and user, pulling from the system pool of UIDs. You may wish to remove the --system argument, although there are usually plenty of system UIDs and GIDs to go around. Finally, we create a directory for the cached objects and ensure ccache running under our instance of pbuilder has sufficient access to create files. The addition of the ccache group should allow other users to utilize the cache when added to said group.
$ ls /var/cache/pbuilder/ccache/ 0 1 2 3 4 5 6 7 8 9 a b c d e f stats $ du -hs /var/cache/pbuilder/ccache/ 23M /var/cache/pbuilder/ccache/ |
If all is well, we'll be greeted by output like that shown above.
Once you've backported an X Window application, you may wish to test it without having to install the package and all its dependencies on your workstation. Fortunately, it is easy to do this from within your pbuilder environment.
First, add the following hook to /etc/pbuilder/hook.d with a filename such as F10vnc to ensure it runs when you actually login to your pbuilder environment.
#!/bin/sh
# Let's plugin VNC!
export HOME=/root
echo force-confold >> /etc/dpkg/dpkg.cfg
DEBIAN_FRONTEND=noninteractive \
SHELL=/bin/true \
apt-get install -y twm xterm vnc4server xfonts-base expect
perl -i -pe 's/^force-confold//' /etc/dpkg/dpkg.cfg
mkdir -p /root/.vnc
cat<<EOF > $HOME/.vnc/xstartup
#!/bin/sh
xsetroot -solid grey
vnc4config -iconic &
xterm &
twm &
EOF
chmod 755 $HOME/.vnc/xstartup
cat<<EOF > /tmp/vnc.exp
spawn vnc4passwd
expect "Password:" { send "vncvnc\r" }
expect "Verify:" { send "vncvnc\r" }
interact
puts "\nYour insecure VNC password is: vncvnc\n"
EOF
/usr/bin/expect /tmp/vnc.exp
vnc4server &
cat<<EOF >/usr/sbin/killvnc
export HOME=/root
ps -ef | grep [X]vnc | \
awk '{ print \$9 }' | \
xargs -l1 -i% --no-run-if-empty vnc4server -kill %
EOF
chmod 755 /usr/sbin/killvnc
echo "Run /usr/sbin/killvnc when you're done with VNC!"
echo "Otherwise, you will get an umount failure for /dev/pts!"
echo
|
Above, we install and configure a VNC server with a default password of vncvnc. As the prompt is interactive, expect is used automate the process of selecting a password as one must be set prior to first use of the VNC server. A script /usr/sbin/killvnc is necessary to shutdown the VNC or you won't be able to exit your pbuilder instance.
To test your backported X application, simply login to your pbuilder.
$ sudo pbuilder login ... # apt-get install gnucash |
In order to start the X application, we will want to connect to the running vnc4server instance. First, if it's not already installed, we must install a VNC compatible viewer, such as vncviewer.
$ sudo apt-get install xvnc4viewer $ xnc4viewer localhost:1 |
With a client installed, we can connect to our VNC server instance as indicated above. The password will be vncvnc if the earlier script was used as-is. Upon logging in, we are greeted with a minimal window manager and a single xterm. From here, we can run our X application that lives in the pbuilder environment.
Once we're done, it's important to run /usr/sbin/killvnc to stop vnc4server so /dev/pts can be successfully unmounted. If you're running pbuilder under a User-mode Linux setup, which isn't covered here, you can skip this step.
Disabling the installation of VNC until you explicitly need it may be a wise idea as it takes some moments for all the necessary packages to be installed, making a quick trip into the pbuilder chroot instance not so quick.
To avoid running as root, which is necessary for pbuilder to create bind mounts and create the device nodes, sudo is suggested. If you aren't already using sudo, let's look at a fast configuration that gives your user access to pbuilder.
# apt-get install sudo # chmod u+w /etc/sudoers # cat >> /etc/sudoers username ALL = /usr/sbin/pbuilder ^D # chmod u-r /etc/sudoers # ^D $ sudo pbuilder ... |
The above will give you permission to run pbuilder as the root user. Be sure to use your actual username. You will need to enter the password for your username when you run sudo, but not again until after fifteen minutes since your last sudo command by default. You can do much with sudo, but the above is sufficient.