Revision 2 as of 2008-02-18 19:22:19

Clear message

Dev Week -- Packaging 101 -- Daniel Holbach -- Mon, Feb 18

(01:02:31 PM) dholbach: ok... let's get started with the next session
(01:02:41 PM) dholbach: It's Packaging 101 time! :)
(01:02:46 PM) dholbach: who's here for the session?
(01:02:50 PM) corevette: me
(01:02:53 PM) charliecb: me
(01:02:53 PM) danbhfive: +1
(01:02:55 PM) jsauer: me
(01:02:55 PM) antcarsa: me too
(01:02:55 PM) zeeth: cprov: thanx
(01:02:56 PM) icicle: me
(01:02:56 PM) nijaba: o/
(01:02:57 PM) InsClusoe: me
(01:02:58 PM) Unksi: o/
(01:02:58 PM) mruiz: _0/
(01:03:01 PM) Kaasethu: me
(01:03:01 PM) barcc: me
(01:03:03 PM) civija: civija++
(01:03:03 PM) rulus: me too
(01:03:04 PM) lengau: Hi
(01:03:09 PM) snhome: me too
(01:03:14 PM) Grazieno: me
(01:03:15 PM) shujin: me sever
(01:03:15 PM) warp10: me too!
(01:03:16 PM) fredre: +1
(01:03:20 PM) dholbach: ROCK and ROLL!
(01:03:23 PM) dholbach: bring it on
(01:03:32 PM) vistakiller: and me :P
(01:03:32 PM) tamrat: \o/
(01:03:33 PM) holloway: me
(01:03:52 PM) zeeth: me \o/
(01:03:54 PM) dholbach: for those of you tuning into the Developer Week just now: please ask your questions in #ubuntu-classroom-chat and prefix them with QUESTION: so they're easier to spot
(01:03:59 PM) dargol: me
(01:04:05 PM) mrevell left the room (quit: "Ta ta").
(01:04:06 PM) eddyMul: me
(01:04:20 PM) amachu: ாை
(01:04:23 PM) amachu: hi
(01:04:39 PM) dholbach: we don't need preparation for this session, just make sure you have a deb-src line in your /etc/apt/sources.list
(01:04:55 PM) dholbach: if you're using hardy, that's just "deb-src hardy main restricted universe multiverse"
(01:05:06 PM) dholbach: once you've added that, please run     sudo apt-get update
(01:05:38 PM) dholbach: we'll take a look at a small package I made a while ago called gnome-web-photo
(01:05:52 PM) dholbach: (it does thumbnails of webpages, etc.)
(01:06:00 PM) dholbach: please run     apt-get source gnome-web-photo     to get the source
(01:06:21 PM) dholbach: the following files have been downloaded:
(01:06:28 PM) dholbach: gnome-web-photo_0.3-0ubuntu1.diff.gz
(01:06:28 PM) dholbach: gnome-web-photo_0.3-0ubuntu1.dsc
(01:06:28 PM) dholbach: gnome-web-photo_0.3.orig.tar.gz
(01:06:48 PM) dholbach: the .orig.tar.gz file is exactly what I downloaded from the upstream webpage
(01:07:05 PM) dholbach: the .diff.gz the compressed patch I needed to add to make it built in the ubuntu / debian world
(01:07:17 PM) dholbach: the .dsc is a description file that has md5sums in it and so on
(01:07:50 PM) dholbach: apt-get source used dpkg-source -x to extract that and give us gnome-web-photo-0.3
(01:08:11 PM) dholbach: ok, let's dive into it
(01:08:16 PM) dholbach: cd gnome-web-photo-0.3
(01:08:18 PM) dholbach: ls debian/
(01:08:36 PM) dholbach: these are the files I needed to add to make it build, ie to "package it"
(01:08:51 PM) dholbach: let's go through them one by one and let me know if you have questions
(01:08:59 PM) dholbach: less debian/changelog
(01:09:25 PM) dholbach: debian/changelog is necessary to indicate which changes were made by whom and which version number was used
(01:09:52 PM) dholbach: as a package maintainer it's necessary to document explicitly what you did
(01:10:09 PM) dholbach: Ubuntu is a bit special in the development modell: we maintain everything as teams
(01:10:26 PM) dholbach: there are of course people who know a great deal about a package, but there's no BML
(01:10:29 PM) dholbach: (big maintainer lock)
(01:10:39 PM) dholbach: so you're free to participate and enhance existing packages
(01:10:54 PM) dholbach: if you do changes, everybody needs to know why you did them :)
(01:11:06 PM) dholbach: <corevette> QUESTION: is there a format for a changelog?
(01:11:10 PM) dholbach: first good question
(01:11:16 PM) dholbach: yes, the format is pretty fixed
(01:11:25 PM) dholbach: let's go through all the items one by one
(01:11:46 PM) dholbach: first up is the source package name (I'll talk a bit about source and binary packages in a bit), in our case it's "gnome-web-photo"
(01:11:55 PM) dholbach: next up is the version string
(01:12:27 PM) dholbach: in debian you'd use <upstream version>-<debian revision>
(01:12:40 PM) dholbach: where upstream version means the version that the software authors just released
(01:13:00 PM) dholbach: and debian revision just being an incremental number (in the simple cases) starting with '1'
(01:13:34 PM) dholbach: in Ubuntu we add "ubuntu<number>" to indicate that we're changing a debian revision of a package
(01:13:52 PM) dholbach: as this package is not in debian (yet), it started with    0ubuntu1
(01:14:12 PM) dholbach: next up is the
(01:14:23 PM) dholbach: ubuntu release we're uploading to
(01:14:33 PM) dholbach: we can always only upload to the one that is under development right now
(01:14:43 PM) dholbach: in our case we could only upload to hardy, but not to gutsy or feisty
(01:15:03 PM) dholbach: (there is feisty-updates and feisty-security and we have an entire session just about that later this week)
(01:15:13 PM) dholbach: the urgency is irrelevant in the ubuntu world
(01:16:25 PM) dholbach: next up are all the individual changes that happened in the new revidion
(01:16:25 PM) dholbach: revision
(01:16:25 PM) dholbach: some people tend to specify changes like this:
(01:16:25 PM) dholbach:  * <file>: <change>
(01:16:25 PM) dholbach: I like that too
(01:16:26 PM) dholbach: if you package a new upstream version it might make sense to point out what happened upstream too
(01:16:33 PM) dholbach: next up is your name and email address plus a date string
(01:16:45 PM) dholbach: it'd be terribly irksome to type this all by hand
(01:17:03 PM) dholbach: which is why we use a tool called   dch  (in the devscripts package) to generate most of it for us
(01:17:18 PM) dholbach: <eddyMul> QUESTION: in debian/changelog, what time should I use for the timestamp? does it matter?
(01:17:31 PM) dholbach: eddyMul: date -R is used by dch
(01:17:39 PM) dholbach: <eddyMul> QUESTION: follow up to corevette: is there an emacs mode for it?  :)
(01:17:55 PM) dholbach: eddyMul: dch will use whatever you specified as EDITOR in your profile
(01:18:06 PM) dholbach: <danbhfive> QUESTION: why the zero?
(01:18:09 PM) dholbach: I think I answered that
(01:18:25 PM) dholbach: <charliecb> QUESTION: Starts the revision-number always with 1 ?(and 0 for debian if the package doesn't exists in debian)
(01:18:47 PM) dholbach: charliecb: if you take a debian package and alter it, you mostly just add "ubuntu1", then increment the last number
(01:18:58 PM) dholbach: of course there are special cases, but we'll not deal with them in this session nos
(01:18:59 PM) dholbach: now
(01:19:10 PM) dholbach: ok, everybody happy with debian/changelog?
(01:19:26 PM) dholbach: ok, party on
(01:19:40 PM) dholbach: next up is debian/compat which in our case just says "4"
(01:20:13 PM) dholbach: we have a set of tools in what we call debhelper suite that help with packaging a lot
(01:20:45 PM) dholbach: lots of tasks like installation of files, compression of changelog, etc - all the kind of tasks you use in nearly all packages - are simplified in there
(01:20:58 PM) dholbach: debian/compat specifies the debhelper compatibility level
(01:21:30 PM) dholbach: it's not really interesting, but for those of you interested in it, check out   man debhelper  to see what those debhelper compat levels mean
(01:21:39 PM) dholbach: it's usually not necessary to alter the value
(01:21:52 PM) dholbach: <mruiz> QUESTION: dholbach, about debian/compat how to decide the version?
(01:22:41 PM) fd is now known as yafd00-00
(01:22:48 PM) dholbach: mruiz: if you package a new piece of software and don't depend on any new debhelper features, just stick with 4 or 5 (which is what dh_make will use for you)
(01:22:54 PM) dholbach: <charliecb> QUESTION: _must_ debian/compat file exists?
(01:23:09 PM) dholbach: charliecb: I don't think it needs to exist, but it does for clarity in the majority of packages
(01:23:14 PM) dholbach: <corevette> QUESTION: how do you use debhelper; tutorial online?
(01:23:18 PM) dholbach: corevette: good question
(01:23:43 PM) dholbach: is THE page you want to check out
(01:24:00 PM) dholbach: it has lots of tutorials, including how to package use debhelper
(01:24:11 PM) dholbach: ok let's move on - debian/control is much more exciting
(01:24:30 PM) dholbach: the first thing you'll notice: it's separate in two stanzas
(01:24:37 PM) dholbach: <eddyMul> QUESTION: is it worth the time and effort to "bump up" a package's debhelper compat? (which probably incurs some changes in packaging code...)
(01:24:56 PM) dholbach: eddyMul: not really, only if you *REALLY* depend on a new debhelper features
(01:25:08 PM) dholbach: in all other cases it's just an upload that has no user impact
(01:25:16 PM) dholbach: coming back to debian/control
(01:25:21 PM) dholbach: the first stanza is about the source
(01:25:35 PM) dholbach: and all the following ones (in our case it's just one) are about the binary package
(01:25:37 PM) dholbach: packages
(01:25:58 PM) dholbach: the source needs to be in a section, so that's specified
(01:26:07 PM) dholbach: a priority is set and a maintainer too
(01:26:14 PM) dholbach: the Maintainer field is quite interesting in the case of Ubuntu
(01:26:56 PM) dholbach: I mentioned before that we maintain packages in teams, but in addition to that our friends at Debian decided that we should change the Maintainer field in cases where we alter packages we inherit from debian
(01:27:25 PM) dholbach: describes the whole process, the general idea is: Debian maintainers received emails of Ubuntu users who wanted to reach the "maintainer"
(01:27:35 PM) dholbach: which did not always work out well
(01:27:52 PM) dholbach: so what we do is, we store the original maintainer, but change the maintainer field to our team mailing lists
(01:28:04 PM) dholbach: next up is "Build-Depends"
(01:28:15 PM) dholbach: and that's one of the most critical values in the whole file
(01:28:27 PM) dholbach: it lists all the packages that need to be installed in order to build the package in a minimal environment
(01:29:07 PM) dholbach: if you listened to the PPA session before you might have gathered that the source is built in chroots (minimal environments) where just build-essential (which includes gcc, make, etc) and the Build-Depends are installed
(01:29:26 PM) dholbach: so there's a distinction between Depends (binary packages) and Build-Depends (source packages)
(01:29:32 PM) dholbach: <tamrat> QUESTION: where can i find information about building different binary packages from one source package (eg creating shared libraries, -dev and the package from the same source)
(01:29:42 PM) dholbach: tamrat: that's exactly in debian/control
(01:29:56 PM) dholbach: I'll just finish up the source stanza in debian/control, then come to the binary packages
(01:30:05 PM) dholbach: <InsClusoe> QUESTION: Why do you say minimal environment?
(01:30:35 PM) dholbach: InsClusoe: you upload the source package (.dsc .orig.tar.gz .diff.gz) to the build daemon, which will take it and put it into a minimal environment, just containing a VERY FEW packages
(01:30:44 PM) dholbach: then install the Build-Depends
(01:30:52 PM) dholbach: so you can't expect that libsomething6-dev is installed
(01:30:56 PM) dholbach: you need to specify it
(01:31:03 PM) dholbach: <dargol> QUESTION: how do I guess build depends for a given source?
(01:31:14 PM) dholbach: dargol: that's an excellent question and what I wanted to show next
(01:31:30 PM) dholbach: of course you could just try and fail over and over again
(01:31:51 PM) dholbach: for that you'd use a pbuilder ( which generates that chroot for you
(01:32:02 PM) dholbach: and watch the build fail over and over again until you get the build-depends right :)
(01:32:29 PM) dholbach: but it's cleverer to check out a few files like ./ which specifies upstream build requirements
(01:32:50 PM) dholbach: in our case the package is written in C++ and uses autotools, so you'll find a there
(01:33:00 PM) dholbach: stuff like:
(01:33:03 PM) dholbach: GLIB_REQUIRED=2.6.0
(01:33:03 PM) dholbach: GTK_REQUIRED=2.6.3
(01:33:03 PM) dholbach: LIBXML_REQUIRED=2.6.12
(01:33:11 PM) dholbach: is usually a very good indicator of what you need to build it
(01:33:46 PM) dholbach: always make sure you mention the -dev packages in the Build-Depends which contain header files necessary to compile and link the software correctly
(01:33:59 PM) dholbach: next up is Standards-Version: 3.7.2
(01:34:15 PM) dholbach: which just declares that this source package complies with version 3.7.2 of the debian policy
(01:34:24 PM) dholbach: <charliecb> QUESTION: this package depends on debhelper. depends *every* package on debhelper?
(01:34:40 PM) dholbach: charliecb: no, but the vast majority - there are people who choose not to use debhelper but do a lot of stuff manually
(01:34:47 PM) dholbach: <eddyMul> QUESTION: how do we decide which ./configure options to --enable and which ones we can do --without?
(01:34:55 PM) dholbach: eddyMul: that's a maintainer question
(01:35:11 PM) dholbach: eddyMul: you maintain the package and make it available for millions of users
(01:35:20 PM) dholbach: it's your (and your team's) call
(01:35:54 PM) dholbach: so if the frobnicator feature is brand-new and you're about to release an LTS, you probably don't want to --enable-frobnicator
(01:36:10 PM) dholbach: ok, let's move on to binary packages
(01:36:28 PM) dholbach: binary packages is the stuff that users like my mom install, they don't care about the source :)
(01:36:44 PM) dholbach: if you have a HUGE package, you might want to consider splitting it up
(01:37:22 PM) dholbach: if you run       apt-cache showsrc mono | grep Binary | head -n 1        you will notice that mono makes heavy use of splitting
(01:37:23 PM) amachu: :)
(01:37:47 PM) dholbach: <eddyMul> QUESTION: ./configure: is there a document about Debian/Ubuntu's default --prefix, etc
(01:38:01 PM) dholbach: eddyMul: I need to pass this question on, best to ask in #ubuntu-motu, sorry
(01:38:23 PM) dholbach: ok, in our case we just have one binary package, it's called gnome-web-photo too
(01:38:35 PM) dholbach: Architecture: is an interesting field
(01:38:56 PM) dholbach: in our case it is "any" which means: build this package on any architecture there is
(01:39:05 PM) dholbach: this means: we upload the source of gnome-web-photo
(01:39:26 PM) dholbach: and it will be built on all buildds: i386, amd64, powerpc, hppa, lpia, etc etc
(01:39:42 PM) dholbach: this is necessary because we compile code that is architecture dependent
(01:39:51 PM) dholbach: that's the case for C, C++ and so on
(01:40:22 PM) dholbach: if you just upload a package that contains artwork, you don't need to bother, set Architecture: all to indicate: it's the same for all architectures
(01:40:32 PM) dholbach: that also includes python and perl scripts, etc
(01:40:44 PM) dholbach: <eddyMul> QUESTION: just to confirm, any != all ?
(01:40:46 PM) dholbach: eddyMul: yes
(01:41:04 PM) dholbach: any = build this for any architecture there is
(01:41:15 PM) dholbach: all = it's the same for all archs
(01:41:24 PM) dholbach: <charliecb> QUESTION: for example, package cdbs has architecture is i386. what if i tell that package gnome-web-photo that architecture is any? is this possible?
(01:41:42 PM) dholbach: charliecb: I think I need to correct you
(01:41:43 PM) dholbach: daniel@lovegood:~$ apt-cache showsrc cdbs | grep Arch
(01:41:43 PM) dholbach: Architecture: all
(01:41:43 PM) dholbach: daniel@lovegood:~$
(01:42:15 PM) charliecb: no, its just an example.
(01:42:15 PM) dholbach: cdbs is written in shell scripts, so there's no need to build it on different architectures
(01:42:43 PM) dholbach: charliecb: then please rephrase your question - I might have misunderstood
(01:43:19 PM) charliecb: go on. i'll ask later.
(01:43:25 PM) dholbach: ok thanks
(01:43:38 PM) dholbach: of course you can have a list of archs the package builds on
(01:43:54 PM) dholbach: so if your package is known to just build on i386 and amd64 (and not on powerpc, etc)
(01:44:09 PM) dholbach: you just write       Architecture: amd64 i386
(01:44:23 PM) dholbach: coming to the next line: Depends
(01:44:29 PM) dholbach: this is really interesting and important to get right
(01:44:44 PM) dholbach: in our case it first looks a bit weird: Depends: ${shlibs:Depends}, ${misc:Depends}
(01:44:59 PM) dholbach: it's not something like:
(01:45:01 PM) dholbach: Depends: libatk1.0-0 (>= 1.13.1), libc6 (>= 2.5-0ubuntu1), libcairo2 (>= 1.3.12), libfontconfig1 (>= 2.4.0), libgcc1 (>= 1:4.1.1-21ubuntu1), libgconf2-4 (>= 2.13.5), libglib2.0-0 (>= 2.12.9), libgnomevfs2-0 (>= 1:2.17.90), libgtk2.0-0 (>= 2.10.3), libjpeg62, liborbit2 (>= 1:2.14.1), libpango1.0-0 (>= 1.15.5), libpng12-0 (>= 1.2.13-4), libstdc++6 (>= 4.1.1-21ubuntu1), libx11-6, libxcursor1 (>> 1.1.2), libxext6, libxfixes3 (>= 1:4.0.1), l
(01:45:01 PM) dholbach: ibxi6, libxinerama1, libxml2 (>= 2.6.27), libxrandr2, libxrender1, gconf2 (>= 2.12.1-4ubuntu1)
(01:45:05 PM) dholbach: we might have expected
(01:45:14 PM) dholbach: the reason for that is simple
(01:45:30 PM) dholbach: ${shlibs:Depends} is a variable that gets expanded during the build process
(01:45:46 PM) dholbach: expanded to all the packages that contain the libraries the binaries in our package are linked to
(01:46:14 PM) dholbach: so the file  /usr/bin/gnome-web-photo   that is in our package is linked to all the libraries contained in the packages above
(01:46:32 PM) dholbach: ${misc:Depends} gets expanded to miscellaneous tools the package might need
(01:46:50 PM) dholbach: for example dh_gconf (in the debhelper) package added gconf2 (>= 2.12.1-4ubuntu1)
(01:46:55 PM) dholbach: <nijaba QUESTION: so for scipts, we need to be explicit?
(01:46:59 PM) dholbach: <nijaba> QUESTION: so for scipts, we need to be explicit?
(01:47:15 PM) nxvl_work: dholbach: i will handle the questions, just ask for next one
(01:47:27 PM) dholbach: nijaba: yes, if your script calls say "wget" or "firefox" you need to depends on that manually
(01:47:49 PM) dholbach: the rest of debian/control is just description
(01:48:02 PM) dholbach: a short one in the  Description:  line
(01:48:06 PM) dholbach: and the long one below
(01:48:13 PM) dholbach: <eddyMul> QUESTION: are ${shlibs:Depends} somehow taking values from Build-Depends?
(01:49:06 PM) dholbach: eddyMul: library packages ship .shlibs files (which are generated automatically), which are used to determine which library corresponds to which library package with which version
(01:49:22 PM) dholbach: on friday we'll have a session about library packaging which talk about that in more detail
(01:49:37 PM) dholbach: ok, party on
(01:49:39 PM) dholbach: debian/copyright
(01:49:47 PM) dholbach: is one of the files most easy to get wrong
(01:50:00 PM) dholbach: debian/copyright contains information about:
(01:50:07 PM) dholbach:  - who did the packaging
(01:50:17 PM) dholbach:  - who has copyrights
(01:50:21 PM) dholbach:  - who is the author
(01:50:30 PM) dholbach:  - under which license the source is licensed
(01:50:51 PM) dholbach: it's absolutely important to get that right, else Ubuntu and Canonical get in deep trouble :)
(01:51:18 PM) dholbach: so if you decide to package something new, you need to make sure you observe all copyrights and check all the source files
(01:51:27 PM) dholbach: else the archive admins won't accept your package
(01:51:57 PM) nxvl_work: <eddyMul> QUESTION: how can I find out what's in ${shlibs:Depends}? is it somehow inferred from Build-Depends? or is `ld*` doing some magic?
(01:52:39 PM) dholbach: eddyMul: ld* is used, then the *.shlibs files of the used packages are looked at, but that goes too far in this sessioon
(01:52:44 PM) dholbach: nxvl_work: next
(01:52:54 PM) nxvl_work: <snhome> QUESTION no author in this one, assumed to be copyright holder?
(01:53:14 PM) dholbach: snhome: if you run    ls src/* | xargs head | less         you will see that there are a bunch of copyright notices
(01:53:28 PM) dholbach: and yes... it is necessary to check all the files beforehand
(01:53:53 PM) dholbach: it's a tedious job, but just imagine the case where you were wrong and something we were not allowed to redistribute ends up on the CDs :)
(01:54:12 PM) dholbach: it's easy to remove software out of the archive, try that with 345743456765434567 million CDs that were sent out :)
(01:54:36 PM) amachu: mmm
(01:54:49 PM) dholbach: ompaul just pointed out that I should refer to
(01:55:01 PM) dholbach: it has a long chapter just about copyright stuff
(01:55:13 PM) dholbach: nxvl_work: next
(01:55:16 PM) nxvl_work: <snhome> QUESTION you said this file contained who did package, has copyright, author but this one has no author. I did not understand your answer
(01:55:38 PM) dholbach: snhome: you are right, I was lazy and the archive admins are stricter nowadays
(01:55:40 PM) dholbach: Copyright Holder:
(01:55:40 PM) dholbach:         Christian Persch <>
(01:55:43 PM) dholbach: is all I wrote
(01:56:07 PM) nxvl_work: dholbach: you are evil!
(01:56:10 PM) nxvl_work: :D
(01:56:10 PM) dholbach: I would have to specify  Upstream Author:  and  Copyright Holder: separately
(01:56:15 PM) ***dholbach hugs nxvl_work
(01:56:23 PM) dholbach: and mention the year of the copyright, etc, etc
(01:56:25 PM) ***nxvl_work hugs dholbach back
(01:56:30 PM) dholbach: there are probably better examples
(01:56:42 PM) dholbach: let's come to the core part of the package: debian/rules
(01:56:53 PM) dholbach: in our case it's tiny, because I decided to use CDBS
(01:57:18 PM) dholbach: CDBS is a set of makefile snippets that internally call all the debhelper tools for us
(01:57:55 PM) dholbach: CDBS has its downsides: there is a lack of documentation ( is good though) and it's not easy to look behind the scenes
(01:58:07 PM) dholbach: and it's certainly not so great for understanding what happens and how things work
(01:58:20 PM) dholbach: but as we're at the end of the session, it fit quite well :)
(01:58:29 PM) dholbach: <eddyMul> QUESTION: debian/rules: is it worth the time/effort to port stuff to cdbs? (I like cdbs. I hope all packages use it)   :)
(01:58:36 PM) dholbach: eddyMul: that's a good question
(01:58:39 PM) dholbach: my answer is: no
(01:59:07 PM) dholbach: first of all: it's of little use to the user, it doesn't really change much and the effort of "porting" to it can be quite big
(01:59:24 PM) dholbach: second: we always need to merge the diff between ubuntu and debian packages
(01:59:51 PM) dholbach: if it's a package you're deeply interested and consider maintaining and think that whatever the debian packages uses is too gruesome, you might do it
(01:59:54 PM) dholbach: but normally, no :)
(02:00:15 PM) dholbach: the session is over - there will be lots of other sessions where you can ask your questions :)
(02:00:28 PM) dholbach: just a few links:
(02:00:29 PM) dholbach: get started:
(02:00:29 PM) dojo is now known as zubat
(02:00:35 PM) nxvl_work: dholbach: thanks for your talk!
(02:00:42 PM) ***nxvl_work waves on dholbach
(02:00:47 PM) dholbach: Blog about Ubuntu Developer Week and your MOTU journey: Add your weblog to by following these instructions:
(02:00:50 PM) dholbach: thank nxvl_work
(02:00:56 PM) charliecb: thx
(02:00:57 PM) Unksi: thankies dholbach :)
(02:00:58 PM) nxvl_work: \o\ /o/ \o\ /o/ \o\ /o/ \o\ /o/ \o\ /o/ \o\ /o/ \o\ /o/ \o\ /o/ \o\ /o/ \o\ /o/ \o\ /o/ \o\ /o/
(02:01:00 PM) dholbach: you guys ROCK! :)