Add an Auto-Incrementing Build-Number to Your Build Process
When building software it's often useful to give each iteration of your build process a unique number. Many IDEs and RAD tools do this for you automatically. If yours doesn't and you're using a make file to build your code you can add an auto-incrementing build number to your project with a few simple changes to your make file.
The mechanism presented here does not need to modify your source code at all, it uses linker symbols to add the build number to your program. Note however that you will probably want to modify your source code to display the build number, but that's not strictly necessary.
Let's start with the following simple make file for building a program:
# Makefile OBJECTS=bnum.o a.out: $(OBJECTS) $(CC) $(LDFLAGS) -o $@ $(OBJECTS)
To add the build number to the make file we set the variable BUILD_NUMBER_FILE to the name of a file that will contain our build number value. Then we add BUILD_NUMBER_FILE to the dependencies for a.out, add BUILD_NUMBER_LDFLAGS to the flags used when linking the program, and finally include the file buildnumber.mak at the end of the make file. The converted make file looks like:
# Makefile # Name of text file containing build number. BUILD_NUMBER_FILE=build-number.txt OBJECTS=bnum.o a.out: $(OBJECTS) $(BUILD_NUMBER_FILE) $(CC) $(LDFLAGS) $(BUILD_NUMBER_LDFLAGS) -o $@ $(OBJECTS) # Include build number rules. include buildnumber.mak
The included file buildnumber.mak looks like:
# Create an auto-incrementing build number. BUILD_NUMBER_LDFLAGS = -Xlinker --defsym -Xlinker __BUILD_DATE=$$(date +'%Y%m%d') BUILD_NUMBER_LDFLAGS += -Xlinker --defsym -Xlinker __BUILD_NUMBER=$$(cat $(BUILD_NUMBER_FILE)) # Build number file. Increment if any object file changes. $(BUILD_NUMBER_FILE): $(OBJECTS) @if ! test -f $(BUILD_NUMBER_FILE); then echo 0 > $(BUILD_NUMBER_FILE); fi @echo $$(($$(cat $(BUILD_NUMBER_FILE)) + 1)) > $(BUILD_NUMBER_FILE)
The linker flags cause the linker to create two symbols: __BUILD_NUMBER and __BUILD_DATE which will be equal to the build number and the build-date respectively. The build-date is set using the standard date command. The build number is simply the value contained in the build number file, which is extracted using the standard cat command.
The make rule for the build number file depends on all the project object files and if any of them changes the build number is incremented by executing the following commands:
if ! test -f build-number.txt; then echo 0 > build-number.txt; fi echo $(($(cat build-number.txt) + 1)) > build-number.txt
The test program bnum.c merely writes out the build number and build-date:
#include <stdio.h> extern char __BUILD_DATE; extern char __BUILD_NUMBER; main() { printf("Build date : %u\n", (unsigned long) &__BUILD_DATE); printf("Build number: %u\n", (unsigned long) &__BUILD_NUMBER); }
A sample of iterative builds is shown below:
$: 24 $: 25
One caveat to an auto-incrementing build number is that just because you have two versions of a program with different build numbers it does not mean they are functionally different. If you routinely run make clean; make all just for fun, you'll get a new build number but nothing will have changed.
Mitch Frazier is an Associate Editor for Linux Journal.
I'm working on a large
I'm working on a large project with multiple modules. We would like to have an incrementing build number for each module but the individual modules don't do any linking in the make file so I'm having trouble setting this up. Do you have any advice to be able to use this without linking or to link from the main make file and increment the individual modules build number when they are built?
Perhaps CPP
You could do a similar thing using the -D option of the C compiler, e.g.
and then have an include file something like this that gets included in every file:
That would give each object file a build number and date. You could also add the file name and put all the version numbers into a separate data section so that all of the build info is in one place.
Consider the following structure:
Change the include file to:
Now you can modify your link script so that all the ".build_info" sections get put together in one place. Add a symbol at the start of the section so that you can obtain the address of the section. Also add a zero at the end of the section so you have a sentinel. With this you can now get a pointer to an array of all the build_info_t structures and print them out if you wanted to know when things were built:
Note, I haven't actually tested the above so I may have some syntax wrong or may have overlooked something important. For one thing check to make sure that optimization doesn't remove the entire structure since it's static and unused.
Mitch Frazier is an Associate Editor for Linux Journal.
What does the -D option do?
What does the -D option do? I'm using an older gcc (4.1.2) and can't find any documentation with the -D option..
-D Option
The -D option does the same thing as #define. It's documented on the gcc man page. Every C compiler I've ever seen has it, and it's one of the most commonly used options when compiling C code.
Mitch Frazier is an Associate Editor for Linux Journal.
Valid for a shared library
Hello,
Imagine the same solution for a shared library. I tested it and it does not work. It seems that it is unable to get the address of the symbol...
when doing nm mylib.so, I have my symbol __BUILD_LIBVER but no way to get it.
Any idea to solve this issue?
Best Regards,
Pascal
Shared Library
When you say "It seems that it is unable to get the address of the symbol", what do you mean? Does the linking fail or the linking succeeds but the value is wrong?
Have you tried adding a function to your library that returns the value? Does that work?
Mitch Frazier is an Associate Editor for Linux Journal.
Thanks for replying... I have
Thanks for replying... I have headache about that! :)
First I want to say your solution is my prefered one compared to a script updating a header file with #define BUILD_NUM XXXX because changing header file changes the dependencies and then need to compile it again for no reason... In complex makefile, I find it difficult to do it. Anyway, it's another discussion...
First I have to say I tested your solution on a standard binary. It works fine.
But I have in charge of a "big" software using a lots of shared libraries. And I would like to use your solution in every artifacts I build (binaries, shared libraries).
So I did a basic test project aiming at verifying it's ok...
and here's the results:
Best Regards
Pascal
Shared Library
You say "random value" in:
Is it random in that it comes out differently each time you run it or is it always the same but wrong? (in which case it's not random)
I suspect the value you're getting is the load address of the shared library plus the value of the symbol. If you take your value "7610387" and convert it to hex you get "742013" which is "742000" plus your version number.
Mitch Frazier is an Associate Editor for Linux Journal.
End of story
You are right.
I created a symbol with a fixed value as a reference and I can retrieve my version from shared libraries. Maybe not the most elegant way but it works!
Thank you so much.
Can you pls elaborate on what
Can you pls elaborate on what you mean by "created a symbol with a fixed value as a reference". I need the value *without* the so load address.
thanks
Fixed Symbol
Use the same method to create a symbol whose value is always the same (zero probably works best), for example:
Now in your code when you want the value of __BUILD_NUMBER, use the value of __LOAD_OFFSET to "remove" the load offset of the shared library, e.g.
Mitch Frazier is an Associate Editor for Linux Journal.
I personally don't like this
I've done a fair amount of work in trying to verify what source object code came from, and guaranteeing that a build of that same source at another point in time will produce identical output. This is largely of interest to a distribution that wants to make sure their source packages recompile as expected.
This sort of "feature" is quite annoying in doing that. lots of packages do similar things to this. There is even a gnu cpp compiler macro that you can use (if forget what it is).
The problem is that identical source input starts to produce binary output that differs every time a second ticks off the clock.
just my 2 cents.
Avoid a circular reference
This is a nice tip.
However, make will complain about a circular reference.
Instead of:
OBJECTS=bnum.o
You may want to do this:
SOURCES=bnum.c
OBJECTS=$(SOURCES:.c=.o)
Then - (note dependency change)
$(BUILD_NUMBER_FILE): $(SOURCES)
@if ! test -f $(BUILD_NUMBER_FILE); then echo 0 > $(BUILD_NUMBER_FILE); fi
@echo $$(($$(cat $(BUILD_NUMBER_FILE)) + 1)) > $(BUILD_NUMBER_FILE)
This will avoid a circular reference. However, you only get a build number increment for source file changes.
Doesn't seem to complain here
I don't get any complaints from make. Did you actually test this and get a complaint from make?
Seems like BUILD_NUMBER_FILE should depend on the same thing that linking depends on, so that every time you re-link you get a new version number. Not relevant for this example, but in more complex cases, depending on the sources would miss changes in header files.
Mitch Frazier is an Associate Editor for Linux Journal.