I asked this question on Stack Overflow earlier, but I think it’s worth posting basically the same thing here too.
I’ve been building libraries, and collections of layered libraries, for a long time, and I’m still not totally happy that I’ve found the best way to organise them. The question I asked was aimed at soliciting some insights from others.
Here it is:
Let’s say that I’ve three libraries, A, B, and C.
Library B uses library A. Library C uses library A.
I want people to be able to use A, B, and C together, or to just take any combination of A, B, and C if they wish.
I don’t really want to distribute them together in one large monolithic lump.
Apart from the sheer issue of bulk, there’s a good reason that I don’t want to do this. Let’s say that B has an external dependency on some other library that it’s designed to work with. I don’t want to force someone who just wants to use C to have to link in that other library, just because B uses it. So lumping together A, B and C in one package wouldn’t be good.
I want to make it easy for someone who just wants C, to grab C and know that they’ve got everything they need to work with it.
What are the best ways of dealing with this, given:
- the language in question is Objective-c
- my preferred delivery mechanism is one or more frameworks (but I’ll consider other options)
- my preferred hosting mechanism is git / github
- I’d rather not require a package manager
This seems like a relatively straightforward question, but before you dive in and say so, can I suggest that it’s actually quite subtle. To illustrate, here are some possible, and possibly flawed, solutions.
CONTAINMENT / SUBMODULES
The fact that B and C use A suggests that they should probably contain A. That’s easy enough to achieve with git submodules. But then of course the person using both B and C in their own project ends up with two copies of A. If their code wants to use A as well, which one does it use? What if B and C contain slightly different revisions of A?
RELATIVE LOCATION
An alternative is set up B and C so that they expect a copy of A to exist in some known location relative to B and C. For example in the same containing folder as B and C.
Like this:
libs/
libA/
libB/ -- expects A to live in ../
libC/ -- expects A to live in ../
This sounds good, but it fails the “let people grab C and have everything” test. Grabbing C in itself isn’t sufficient, you also have to grab A and arrange for it to be in the correct place.
This is a pain - you even have to do this yourself if you want to set up automated tests, for example - but worse than that, which version of A? You can only test C against a given version of A, so when you release it into the wild, how do you ensure that other people can get that version. What if B and C need different versions?
IMPLICIT REQUIREMENT
This is a variation on the above “relative location” - the only difference being that you don’t set C’s project up to expect A to be in a given relative location, you just set it up to expect it to be in the search paths somewhere.
This is possible, particularly using workspaces in Xcode. If your project for C expects to be added to a workspace that also has A added to it, you can arrange things so that C can find A.
This doesn’t address any of the problems of the “relative location” solution though. You can’t even ship C with an example workspace, unless that workspace makes an assumption about the relative location of A!
LAYERED SUBMODULES
A variation on the solutions above is as follows:
- A, B and C all live in their own repos
- you make public “integration” repos (lets call them BI and CI) which arrange things nicely so that you can build and test (or use) B or C.
So CI might contain:
- C.xcworksheet
- modules/
- A (submodule)
- C (submodule)
This is looking a bit better. If someone just wants to use C, they can grab CI and have everything.
They will get the correct versions, thanks to them being submodules. When you publish a new version of CI you’ll implicitly be saying “this version of C works with this version of A”. Well, hopefully, assuming you’ve tested it.
The person using CI will get a workspace to build/test with. The CI repo can even contain sample code, example projects, and so on.
However, someone wanting to use B and C together still has a problem. If they just take BI and CI they’ll end up with two copies of A. Which might clash.
LAYERED SUBMODULES IN VARIOUS COMBINATIONS
The problem above isn’t insurmountable though.
You could provide a BCI repo which looks like this:
- BC.xcworkspace
- modules/
- A (submodule)
- B (submodule)
- C (submodule)
Now you’re saying “if you want to use B and C together”, here’s a distribution that I know works.
This is all sounding good, but it’s getting a bit hard to maintain. I’m now potentially having to maintain, and push, various combinations of the following repos: A, B, C, BI, CI, BCI.
We’re only talking about three libraries so far. This is a real problem for me, but in the real world potentially I have about ten. That’s gotta hurt.
So, my question to you is:
- What would you do?
- Is there a better way?
- Do I just have to accept that the choice between small modules and a big monolithic framework is a tradeoff between better flexibility for the users of the module, and more work for the maintainer?
Often, when I’m contracting for someone, I find myself asking them lots of things.
Sometimes I do it formally by writing a spec, and saying “this is what you want, right?”. Other times it’s just a conversation.
Then, equally often, I find myself apologising for asking so many “stupid questions”.
Then I tell myself off for being insecure and apologising.
I like this post by Matt Gemmell on how to manage your email.
He describes pretty much what I’ve been doing for the last few years.
I’m not as good as I should be about replying quickly to the people who do matter, but I’m considerably better than I used to be since I started being more ruthless about the others.
The only thing I’d add to Matt’s suggestions is to do the following:
- make a group in Address Book / Contacts
- add the important people to it
- make a new Smart Mailbox that just shows mail from people in this group
- change Mail’s badge to show the unread count from this mailbox
- change Mail’s notification sound to only fire for this mailbox
- reduce the frequency that it checks mail - does it really matter if you don’t get it for another half an hour?
In Mountain Lion, the new Messages app replaces iChat.
If you’re the kind of person like me who religiously keeps your email, chat logs, etc, but you have chosen not to do a migration of your data when upgrading to Mountain Lion, you’re going to be faced with the problem of how to import your old iChat logs into Messages.
It turns out that Messages stores logs in:
~/Library/Messages/Archive
They appear to be the same format as iChat, so if you move your old logs into this folder, Messages will find them.
Alternatively, you can make a symbolic link to a folder elsewhere and put your logs in there. For example, this will rename the old Archive as “Archive.old”, then make a link to a folder called “iChat” in your Dropbox folder.
> cd ~/Library/Messages
> mv Archive Archive.old
> mkdir -p ../../Dropbox/iChat
> ln -s ../../Dropbox/iChat Archive
This just leaves the Archive.old folder lying around, but you could obviously move its contents into your new iChat folder then delete Archive.old.
We’ve been pretty busy in recent months, with a number of contracting jobs, including a bit of work with the lovely folks over at Karelia software.
Live music is dear to our hearts though, so we’re planning on taking some time out this week to attend the excellent Hebridean Celtic festival, which happens each year here in Stornoway, on the Scottish island of Lewis in the Outer Hebrides.
The festival is very much a labour of love, and although it attracts a large audience from all over the world, it’s run by a charitable trust. So when they came to me to ask about the possibility of doing an app, I was rather keen to get involved (although a little scared at how little time we had to get something ready for this year).
After a bit of nifty footwork, and a few anxious nights waiting for Apple’s review process, the result is this little app. Given the amount of time we had, we decided to keep it simple, but we’re rather pleased with the results. Hopefully, the festival organisers are too.