When you develop software, you’re always dealing with dependencies, and, if you’re lucky (or have made a quite enlightened choice of dependencies), you don’t have to worry too much about version numbers. But what if you do?
I won’t want to talk about dynamic link libraries (because I stopped coding for Windows a long time ago already) or shared object (because I don’t know everything there’s to know about them just yet) but only about source code.
Ideally, you’re working with a revision control software that makes versions and revisions quite explicit. Testing or checking out a file will usually give you some kind of version information message, such as ‘revision 42’ or ‘branch 2.1, v 1.54’ or whatever, so you know exactly what version, branch, etc., of source code you’re dealing with. However, you’re not always so lucky.
Consider the following file:
/* Copyright (C) 1991-1993,1995-2006,2007,2009 Free Software Foundation, Inc. This file is part of the GNU C Library. The GNU C Library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The GNU C Library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with the GNU C Library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ #ifndef _FEATURES_H #define _FEATURES_H 1 /* These are defined by the user (or the compiler) to specify the desired environment: __STRICT_ANSI__ ISO Standard C. _ISOC99_SOURCE Extensions to ISO C89 from ISO C99. _POSIX_SOURCE IEEE Std 1003.1. ...another 350-ish lines of code....
(it’s an excerpt from <features.h> found on my Ubuntu 10.04 box with gcc 4.4.3.) Can you spot what’s wrong with this header? It states the version of the GPL (for which I have mixed feelings—another story) but not the version of the library, nor the revision of this particular file. And while you can assume that between minor versions revisions are minor, and essentially harmless, they still can have the effect of breaking your code in unexpected ways, so versions are important.
Now, I don’t have any fool-proof solution to this problem just yet; but I’ll expose you what I have thought of so far (and feel free to comment or suggest different methods). I think that any source file should have a macro (despite macros being evil) that defines its version explicitly. Probably something reminiscent of include guards, something like __THIS_FILE_VERSION__ that would contain a numeric version such as 42 for 4.2, or maybe 421 for 4.2.1? That’s a possible solution given the severe limitations in the preprocessor’s ability to manipulate string-like symbols, but is it a sufficient solution?
Such #defines can be put manually (and thus error-pronely) into the files at release time, but it would be much better to have them automagically inserted by the revision control system at checkin/checkout. Say a full comment and a define like:
#ifndef __ATOMIC_TOASTER_H__ #define __ATOMIC_TOASTER_H__ /* file atomic-toaster.h checked out on 'Thu Aug 12 20:08:27 EDT 2010' by user 'steven' version 2.41 (branch 'xyz') */ #define __ATOMIC_TOASTER_H_VERSION__ 241
(Although, realistically, I would expect such a system to add doxygen-like tags to be somewhat robust to user edition. It’s rather hard to have your files not messed up by the editor: spaces changed to tabs, indentation changes, etc.) You could then test explicitly the version requirements:
#if defined(__ATOMIC_TOASTER_H_VERSION__) \ && ( __ATOMIC_TOASTER_H_VERSION > 410 ) ... #else #error dependency on atomic-toaster.h not met #endif
While I do not think that what I suggest is fool-proof, I believe that version-related compatibility issues can be partly enforced by external scripts (buildscripts, automake, and the like) but also at source-level using, maybe, language extensions in a design-by-contract mindset where part of the contract is the version numbers.
What’s your take on this? Suggestions? Ideas?