Download source files - 33 Kb
When performing builds, it is useful to have the build number, the low-order 16 bits of the version number, automatically increment. This helps you track releases. However, Microsoft does not provide any mechanism in the build system for doing this. Consequently there have been a number of solutions, all of which I found unsatisfactory for a variety of reasons I will discuss later. The result of my dissatisfaction, and the initiation of a new major project, finally pushed me over threshold, and I wrote my own. It, too, has problems. However, it has a set of problems I deem more acceptable than the other solutions. You may or may not like this solution, but that's up to you.
My first solution (obsolete)
In a past life, I had an automatic version stepper that ran as part of my hand-constructed
makefile. It ran a little program called "
version" that read a file
version.h, incremented the version number following the period, and wrote the file back. I could then use this version number string any way I wanted to. It also wrote an accompanying file
version.ver that was used by my editor to insert the version number in a change log. This worked for years. The one downside was that the file that depended on
version.h got recompiled on each build, so I usually made it a small file containing only a
sprintf in a subroutine.
#define VERSION_NUMBER "1.361"
Then along came Visual C++. The Good News is that since I started using it I haven't had to look at the insides of a
makefile. The Bad News was that until the latest releases there was no way to add custom build steps effectively. I had to run my little
version program manually. Now had the opportunity to add an automatic step. But what to put in the step?
Stamping the Executable
I first went to the new, improved L CodeGuru site. I searched for solutions there. One solution was a program that went in and rewrote the
VERSIONINFO resource in the executable. I considered this risky, and the number of supplemental comments about it, such as the failure to recompute checksums causing problems, dissuaded me from this. Besides, I'd done this years ago (and I had recomputed the checksums) and I consider it a fairly fragile mechanism (in the case I cited, I was writing license information into the executable during an install, including an encrypted password). So I ruled it out, although it is in the abstract the nicest mechanism because it does not force a recompilation or reload of resources.
Another mechanism was a VBA subroutine that could be automatically invoked by the compilation mechanism. This would read the resource file in as text, change it, and write it back. It had some reported problems and some fixups, but it had the problem that it incremented the number on every build, successful or not. It also had the problem that it would close the resource file, then reload it as a text file, modify it, and write it out. It would then delete the text version of the resource file. If you take my solution and modify the
.rc file, the effect, although obtained in a different fashion, will be the same. But I have a workaround.
Another solution was a VC++ add-in. This looked really cool, but, alas, it did not have any accompanying source code, just the DLL file. So I decided to pass on this. I might have considered it if the source code had accompanied it.
I then thought about doing my own add-in. A brief perusal of the add-in documentation convinced me that, as aesthetic as this sounded, it exceeded my effort threshold. Since the "meter is off" on my effort on this tool, it would generate no revenue, and in addition I couldn't take the time I should to develop it because I do have a number of deadlines yet to be met.
My own solution
Having eliminated the various existing solutions, I decided that I wanted to do my own. That was the only way I could get the solution I wanted. This is a program that runs as a pre-link step.
The problem here is that if I modify the
.rc file, then the
.res file is older than the
.rc file, and this will force a rebuild, as well has having VC++ keep prompting me that there are files that need to be rebuilt before I run. Of course, I could say No each time, but this opens the chance for error as I fail to read the actual contents. So I wanted to do this in a way that wouldn't trigger such spurious rebuilds or messages.
The program is called
buildinc. Put the executable in some useful tool directory you keep for such purposes, or build it in its own directory. It takes two command line arguments, the name of a
version.ver file and the name of the the
.rc file containing the
VERSIONINFO structure. Exactly two arguments, in that exact order.
version.ver file does not exist, one is created. The initial version value is set to
1, 0, 0, 0
so if you run this on your current
.rc file the version will be reset. Make a note of the version you want, and then go in and edit the
version.ver by hand to change the value.
The syntax of the
version.ver file is a series of lines of the form
verno\tyyyy mm dd hh:mm:ss (www mmm-dd)
where verno can have one of two forms
num, num, num, num
Note that spaces are optional but are not retained. If the version number contains only commas, it will be regenerated in the comma-form above, which has spaces following the commas; if it contains any periods, it is regenerated in the period-form with no spaces.
Note that if you put a number of the form 220.127.116.114 in for the binary file version number, the resource compiler seems capable of parsing it and storing the correct value. So I didn't make an effort to use one form for the string representation of the file version and a different format for the binary version.
My program scans the
.rc file for a line of the form
and replaces it with
and also for a line of the form
and replaces that line with
VALUE "FileVersion", "newversion\0"
If you have multiple resources (localization) in your file, it will find the latter line and replace it for all supported languages. However, I do not support multiple input files in this version, so you'd have to rewrite the program to accept multiple input files if you need that support.
Why do I use
version.ver as the definitive source of the version number, and not the resource file? Because I want two features: a continuing log of version generation dates, and because my editor understands how to read a
version.ver file in and add the current version number to change log entries. Also, because I actually moved the
VERSIONINFO resource to the
.rc2 file, it means that I only have to edit a single text file to change the version number by hand, for example, to change any of the initial three fields.
Having thus created a new version number, the program then modifies the
.rc file. Because I am somewhat paranoid about this sort of thing, and based on the various bug reports that were complaining about the other tools, I decided to use the following algorithm:
- Take the build number and create a file name based on the
.rc file and the build number, for example, if the build number was
202, and the
.rc file was
myproject.rc, I would generate the filename
- Subtract 10 from the build number and delete any file by that name, thus I would in the above example delete the filename
myproject.rc.0192. This keeps the space from becoming too cluttered, but gives me a number of backup copies.
- Rename the current
.rc file to that filename.
- Read that file and create a new
.rc file with the updated build number.
I then created the pre-link build step:
buildinc-path\buildinc.exe version.ver myproject.rc
for the project called "myproject". This is half the job. Then I went to the
Resources tab, highlighted the command line shown there, and copied it. I then created a second pre-link step,
rc pasted-commands-here myproject.rc
This forces a recompilation of the resource file that I've just changed, so I won't get any spurious rebuild notifications, or get into an infinite rebuild loop.
This is shown below, for the Debug settings. Certain areas have been blanked to protect proprietary information. You must remember to do this for both the Debug setting and the Release setting, and of course for any custom settings you may have. Don't forget to change the directory in the second step to point to the correct directory (if you do cut-and-paste from one custom pre-link step to the other you could forget to change this).
I use the
version.ver file as the definitive version number representation, rather than the
VERSIONINFO resource. It would be a minor rewrite to change this, but I decided that this was the solution I wanted. Of course, since you have the source, if you don't like this solution you can easily rewrite it.
It also requires a manual rebuild of the
.rc file as part of the pre-link step, which means that if I need to change the include path or otherwise modify the behavior of the
rc compilation step I must remember to do it not only in the
Resources tab but in my pre-link step. In five years of using VC++ I've never modified the
Resources build, so I don't think this is going to bother me. Your Mileage May Vary.
It modifies the
.rc file externally, which forces a reload after each successful build. II consider this the single worst misfeature of what I've done. You may find it tolerable, or hate it. One way around this, which is what I have done, is to move the
VERSIONINFO resource to the
res\project.rc2 file, and specify the
.rc2 file as the target file for
buildinc. This means you can't edit the
VERSIONINFO file in the resource editor, but this may not be a problem, since the number of times I actually edit it (other than changing the version number) are so few as to compensate me for not having to reload resources each time.
I built this as an MFC Console App. This is because I was lazy. I wanted
CStrings, but I needed a console app. Again, I decided that learning STL was not something I wanted to do this past weekend, so I just used MFC. If you feel like rewriting this using STL, feel free. If you send me a copy, I'll even put it up for others (include whatever credit lines you want in the source as well...).
Change logs are very important to me. I have hundreds of projects, a significant number of which go out to other people, who have paid for them. I need to maintain change logs to maintain my sanity (the latter maintenance problem is the dominant one, although many contend that this maintenance has failed utterly). Years ago, specifically, 8-Aug-85 (according to the change log in my change log program), I created a fast, efficient way to handle this. I use the Epsilon text editor from Lugaru.
My opinion of the VC++ editor is that it is a barely acceptable first attempt, but has a long way to go before I'll live in it. It confuses some fundamental concepts like "Window" and "Viewport", and it is not a keyboard-friendly editor. I can type 70wpm, and I resent the disruption of typing speed required by having to use the mouse. I've been using EMACS-class editors for over 25 years, and they seem well attuned to the keyboard-based editing task, and I am well-attuned to them.
In one keystroke combination I can install a change log; with another, I can add an entry. The line wrapping is automatic. I have tools that will do things like process the change logs and consolidate changes by change number, and summarize the number of change lines I add. I have a single keystroke that inserts the change request number on a line affected by the change. Here's a change log created in one of my applications:
Note the [string] added by reading the first line of the
version.ver file. The REQ #127 indicates the change is the result of a modification request, specifically, number 127. This is added by another keyboard keystroke request. Hence I know to the build level when a change was added. While intermediate builds don't matter much, this together with an
awk script yet-to-be-written, will allow me to present a customer with a list of changes applicable to each release version. This makes it easier to track versions for customer support. It also makes it evident which release builds have which change requests in (the change requests are sent to me by the customer; I assign a change number and send back the updated change number file).
awk script tracks the changes by producing a list of the change line summaries, by filename, and a total line count, for example
REQ #133 | (11)
./.\crosspnt.c: * 20-Dec-99 | [1.387] REQ #133: Handle Delete key
./.\FLTRDLG.C: * 22-Dec-99 | [1.387] REQ #133: Fixed display bug for console format
This tells me that change request #133 involved two files,
FLTRDLG.C, with the dates and changes. Since this predates my new
buildinc app it uses the older version.build display. It involved 11 lines of changes.
After it analyzes all the files, I also get the summary report:
Total REQs 210
Change lines 24446
Total source lines 133481
Total files 690
Over the five-year lifetime of this project, from the initial product release in 1997 through the present, there have been 24446 source line changes or additions, represent the usual maintenance activities, such as bug fixes and feature additions. Adding Internet support to this product actually accounted for about 11,000 lines of change, or nearly half of the changes.
Anyone who wants a copy of the EEL source code (it won't do you any good if you're not an Epsilon user) for any of my tools, or my
awk scripts, is welcome to have one. Just don't resell them. Send me email.
This is another tool in the continuing saga of build-step incrementers. I think I like it. Time will tell. I will post changes on this Web site and notices of the changes to the
The views expressed in these essays are those of the author, and in no way represent, nor are they endorsed by, Microsoft.
Send mail to firstname.lastname@example.org with questions or comments about this article.
Copyright © 2000, <!--webbot bot="Substitution" s-variable="CompanyLongName" startspan -->The Joseph M. Newcomer Co.<!--webbot bot="Substitution" endspan i-checksum="64444" --> All Rights Reserved