
Compile-time version strings in CMake - luu
http://www.mattkeeter.com/blog/2018-01-06-versioning/
======
MaxBarraclough
A useful post. A pity that CMake's scripting language is so atrocious, but
it's good to see it's capable of this kind of thing.

Personally I tend to go the route wmu mentions in their comment: add a file
named MyProject.h.in, to expose CMake variables in a generated header file. I
use the C preprocessor's stringification [0] to transform version numbers into
strings. My approach (really just CMake basics) doesn't expose any git
specifics though - it's more basic than what the blog describes.

The MyProject.h.in file looks like:

    
    
        #define MyProject_VERSION_MAJOR @MyProject_VERSION_MAJOR@
        #define MyProject_VERSION_MINOR @MyProject_VERSION_MINOR@
    
        #define EXPAND_AND_STRINGIFY(s) STRINGIFY(s)
        #define STRINGIFY(s) #s
    
        #define MyProject_VERSION_MAJOR_STR EXPAND_AND_STRINGIFY(MyProject_VERSION_MAJOR)
        #define MyProject_VERSION_MINOR_STR EXPAND_AND_STRINGIFY(MyProject_VERSION_MINOR)
    
        #define MyProject_VERSION_STR MyProject_VERSION_MAJOR_STR "." MyProject_VERSION_MINOR_STR
    
    

Getting CMake to process this file is simple enough: we just add
_configure_file(MyProjectConfig.h.in MyProjectConfig.h)_ to the CMakeLists.txt
file.

[0]
[https://gcc.gnu.org/onlinedocs/gcc-4.8.5/cpp/Stringification...](https://gcc.gnu.org/onlinedocs/gcc-4.8.5/cpp/Stringification.html)

------
wmu
Instead of creating C code inside cmake scripts it's better to use command
configure_file
([https://cmake.org/cmake/help/latest/command/configure_file.h...](https://cmake.org/cmake/help/latest/command/configure_file.html)).
The command creates a source file based on a template file.

~~~
nickez
That doesn't run at compile time though.

~~~
MaxBarraclough
I'm not sure what you mean here. It should use CMake's usual change-
detection/dependency-tracking mechanism. [0]

The generation of the .h file from the .h.in file _does_ happen at compile-
time. (Well, at _build_ time, right before the compiler is invoked.)

[0]
[https://stackoverflow.com/a/24246566/](https://stackoverflow.com/a/24246566/)

------
altmind
Modifying the source files in the build may break distributed builds(distcc)
and cache(ccache). Maybe pass git version as defines
add_compile_definitions/add_definitions aka (-DGITVER=yourvar) ?

------
ridiculous_fish
I do not understand the "dummy output" technique. Since nothing depends on
_version.cpp, why does its existence force the command to be re-run?

The usual technique is to have some script check the git version, then output
two files: the version.cpp file, and a "witness" file which changes every run.
The script should be careful to not perturb the mod date of version.cpp unless
it has changed.

This is coupled with Ninja's "restat" option. Without that option, Ninja will
observe that version.cpp will be recreated and schedule all of its dependents
(such as linking). With that option, Ninja will behave more like Make, check
if the file was actually modified.

See CMake's (weirdly-placed) docs on this:
[https://cmake.org/cmake/help/v3.4/policy/CMP0058.html](https://cmake.org/cmake/help/v3.4/policy/CMP0058.html)

Here's fish shell's version gen script: [https://github.com/fish-shell/fish-
shell/blob/master/build_t...](https://github.com/fish-shell/fish-
shell/blob/master/build_tools/git_version_gen.sh)

------
awinter-py
versioning has always felt like something that should be first-class in
programming languages, but isn't because language + buildsystem + VCS aren't
tightly coupled

package managers, for example, should know the git sha

------
beefhash
Once upon a time, we had version control systems that provided keyword
expansion for something like %G% (SCCS, BitKeeper) or $Id$ (RCS, CVS come to
mind). With git, this has fallen out of fashion, however, but I'm not sure
why.

~~~
vhakulinen
Is that somehow fundamentally different from "git describe --tags --always"?

~~~
goatinaboat
Yes. When you checked a file into VSS it would expand the string $Id$ before
actually writing the file into the repo, so it would be in the file you
checked out. So if you had a copy of that file, you could know instantly what
revision it was from. If you had blah.c on your disk and you had a Git repo
it’s far harder to say “what commit did this copy of the file actually come
from”?

------
gumby
This is better than the canonical approach of using CMake's configure_file as
that runs at compile time while this runs at configure time. The problem with
generating your version file at compile time is that it is always newer than
an existing output, therefore every make causes a relink/re-ar.

------
nemetroid
If you're executing the script with add_custom_command, what's the benefit of
writing the script itself in CMake's scripting language rather than (e.g.)
bash or Python? The CMakeLists.txt file is treating the script as a black box
anyway.

