Search code examples
c++linuxbashmakefilebazaar

Inserting terminal output into source file before compilation


I've got a C++ project that happens to be stored in a Bazaar repo. This project also uses a #define'd string to display its version number. Someone just asked if we could simply tie this displayed version number to the bzr repo revision number.

So, in pseudo-C, pseudo-bash, something like:

#define VERSION_STRING "revision $(bzr revno)"
//...
cout << "Starting " << VERSION_STRING;

Or so. How might you answer this question? Does the makefile run a script that inserts the output of that command into the appropriate source file? Is there a macro solution for this? Etc?

I'm open to any and all clever solutions, as I'm drawing an educated blank on this one. =D


Solution

  • The compiler will have a flag to define a macro value externally. For g++ and clang++ this is -D:

    g++ -DVERSION_STRING="revision $(bzr revno)" file.cpp -c -o file.o
    

    To get that in the file as a string, you can either add extra quotes into the definition:

    g++ -DVERSION_STRING="\"revision $(bzr revno)"\" file.cpp -c -o file.o
    

    or you need to know how to stringify that value inside the file, which takes a little magic:

    #define STRINGIFY_HELPER(X) #X
    #define STRINGIFY(X) STRINGIFY_HELPER(X)
    

    Then you could also have a default value. I'd recommend have a different variable set by the compiler to the one you use internally, it helps keep track:

    #include <iostream>
    
    #define STRINGIFY_HELPER(X) #X
    #define STRINGIFY(X) STRINGIFY_HELPER(X)
    
    #ifdef VERSION
    #define VERSION_STRING STRINGIFY(VERSION)
    #else
    #define VERSION_STRING "0.0.0"
    #endif
    
    int main()
    {
        std::cout << VERSION_STRING << '\n';
    }
    

    results in:

    $ g++ -DVERSION="1.0.0" SO.cpp
    $ ./a.out 
    1.0.0
    $ g++ SO.cpp
    $ ./a.out 
    0.0.0
    

    Note, $(bzr revno) is the syntax to run bzr revno and substitute its output in a shell (bash syntax, probably the same in most others). From within a makefile, as musasabi pointed out, the syntax is slightly different: $(shell bzr revno),