UPDATE:  In the comments I’ve seen some people saying that this doesn’t work for them.  One thing that I discovered recently is that, in order for this to work, the URL specified in your schema.xml file MUST MATCH the List attribute on all your Field references in field.xml and schema.xml.  If these don’t match, it will not work.  Hope that helps the folks that were having trouble.

I've seen a lot of statements recently that say the only way to add a lookup column through a feature is through code.  There are even some interesting solutions around this using Feature Receivers out on codeplex here and here.  When I first started hearing these statements I didn't believe it, so I decided to try to build a feature using only CAML that will create a list with a lookup column.  As it turns out this isn't really that difficult, but it did take some digging.  When you are doing this there are two place that you have to be concerned about.  The first is when defining the column as a site column.  When you define content types and custom list schemas, you should first start with your site columns.  I usually place these in a file called fields.xml, this is also what you will see if you take a look through the out of the box list schemas.  Here is an example of one of my site columns of the Lookup type

<Field ID="{2FF1B484-6D70-449c-8E5C-904E4D5971E1}" Name="Leader" Group="My Custom Columns" Type="Lookup" DisplayName="Leader" List="Lists/Leaders" ShowField="Title" PrependId="TRUE"/>

There are three properties here that are not found on a standard site column definition.  The first is "List", this, as you might have guessed, points to the URL of the list in the site.  In this example I know I am looking for the Title column in a list that is found under Lists/Leaders.  The next property to pay attention to is "ShowField", you guessed it, this is the field that you want to show in your lookup column.  The last property is "PrependId".  I'm not 100% sure on this but I think this has something to do with whether the lookup column returns the id in front of the value.  The out of the box columns set this to "TRUE" and I do like to get back my item ID with my value so I set it to TRUE as well.  Disclaimer: I never tested what setting it to "FALSE" does.

The next thing you will want to do is add this column to a custom list schema that you define.  If you aren't familiar with custom list schemas Ted Pattison's book Inside WSS 3.0 is a great place to get familiar with this.  Quickly summarized, whenever you create a custom list, there must be a schema.xml file in a folder that is the same name as the list name.  That sounds kind of confusing, but describing how to build custom list schemas is out of the scope of this post.  Ted's book explains this really well however, and if you are a SharePoint 2007 developer and haven't read this yet, go buy it.  Anyway, in the custom list schema one of the first sections is called "Fields".  In this section, if I wanted to add my custom lookup site column to my custom list I would use the following line of CAML.

 

<Field ID="{2FF1B484-6D70-449c-8E5C-904E4D5971E1}" Name="Leader" Type="Lookup" DisplayName="Leader" List="Lists/Leaders" ShowField="Title"/>

Again, if you haven't worked with this stuff before this might seem kind of redundant.  Truthfully it is, but that is just how custom list schemas work, so get used to it.  Really, that is all there is to it, everything else you want to create (content types, list templates, list instances) is all the same for lookup columns as it is a standard text column.

Hope that helps to clear up some confusion around lookup columns.