After starting work on appledoc redesign, one of the first things I added was command line parsing. I used trustful DDCli library from Dave Dribin. However I soon discovered it doesn’t work well with arc. That, coupled with different workflow I wanted, prompted me to digg in Dave’s code to see how I could change it to suit my needs better. At the end I ended with writing a command line interface library from scratch.
Command line parsing
DDCli is built on top of
getopt_long. So my first attempt was to use the same function in my library too. Using man page and Dave’s code as the example, I quickly came with a working solution. But either I was doing something wrong or
getopt_long has some failing (in 10.7.3 at least) - it wasn’t reliable: it was working most of the time, but here and there it didn’t report all my command line arguments. I couldn’t figure out the reason for this. At first I though to just leave it like that, but it bothered me - even in my quick tests, I could notice it couple of times, so it would be much more noticable once more people start using it.
So eventually I came to conclusion to try implement custom parsing - after all it’s not really a rocket science… Turns out, it really wasn’t difficult at all. And it even brought a nice side effect: the code became cleaner too (using
getopt_long can be quite messy).
Application wide settings
DDCli uses really simple and clever mechanism for passing command line arguments back to the application - KVC! Not only this nicely decouples the code, it also takes care of type conversion. However in appledoc, this decision complicated my code somewhat - appledoc not relies on command line only, but also on global and project settings files. Each level of settings overrides previous one and command line overrides all. But as users can supply paths to global and project settings via command line, I had to preprocess it to properly handle all levels.
Many times, command line values are stored inside a helper settings class that is then passed on to various application components, similar to
NSUserDefaults. As the class includes a lot of boilerplate code, I included one in GBCli!
I also wanted to make various settings levels more explicit. Although most command line tools only use command line, most of them would benefit from using at least two levels: factory defaults and command line settings. And more complicated tools, such as appledoc, should be able to easily add support for additional levels. This can be easily accomplished with GBCli. You can even save or load settings from file - it’s all built in! Or, if you choose so, you don’t have to deal with settings hierarchy at all and just work with them as you would with
I decided to use block based API instead of KVC. It doesn’t auto-magically convert types, but this can easily be implemented through custom properties. I even included helper macros for synthetizing those for you.
Most command line tools include help output which prints out all possible switches and short descriptions. Being good citizen, I also included this to appledoc. Besides that, I also included optional printout of all settings values being used for current run - this provides nice debugging information. However I didn’t have information which settings level the values came from - was it factory default, global or project settings file or command line. Plus, for every new command line switch, I had to manually update registration to DDCli and then update both, help and values printout code. This was maintenance nightmare and I frequently forgot to update one or the other (sometimes even both :).
With GBCli, I wanted to make this more streamlined: just register all settings (which you would need to do anyway) and have the library handle all the rest. Net result is: you register the settings in a single place and all the rest is automatically done for you!
And one of the consequences of using explicit settings levels is being able to print out which level certain value comes from - similar to how Xcode splits build settings to Mac OS Default, Project and Target. This can provide very useful debugging information and I wanted to include something like that in appledoc for a while. Now I can, and so can you :)
The new appledoc (still in the very early phase at the moment of this writing) uses GBCli for all its settings needs. And while appledoc may require somewhat more complex settings behavior, GBCli itself is quite flexible and can cover various use cases. Check it out and see if it would work for your tool too!
GBCli library is released under MIT license and is available on GitHub. See accompanying readme file for more information and example of usage.
Special thanks to Dave Dribin and his DDCli for inspiration!