CMAKE_CONFIGURE_DEPENDS-like option for external commands
I'm opening this issue for discussion about CMAKE_CONFIGURE_DEPENDS-like option that would for invoking external commands, not only files.
This is a feature that, as far as I can tell, is not possible with the current tools CMake has; but it is necessary for various version control systems (and possibly other things). The issue I'm facing is that I want CMake variables that hold current git commit hash and other info (for example if the working directory is dirty, which is the hardest).
Having these in variables is nice, because I can then easily generate output files for archival, that bear their respective commit hash in file name.
For git, a relatively simple way to do most of it is to set CMAKE_CONFIGURE_DEPENDS
to .git/index
and .git/HEAD
. This triggers reconfigure on branch changes and commits. However, it does not trigger on working directory "dirtyness"; it can not, as git is not necessarily invoked. Other version control systems are not even that lucky, for example Mercurial doesn't have index and HEAD files (that I know of), so this is a problem for even more basic info (like the commit hash itself).
My idea was to call an external command (currently git diff-index --quiet HEAD --
) to get this information, and put it into a file that is listed in CMAKE_CONFIGURE_DEPENDS. However, I haven't found a place to do that, as the check itself is done way too soon and once the regular targets start executing, it is too late. The reconfigure is triggered on the next build, which is too late as there already is build with the wrong information.
So far I haven't been able to work around this in a clean way. I have a few ugly ideas that would work, but I'd be afraid they'll break:
- I can compare the info at build time, and fail the build that's not consistent. I can then either instruct the user to build again (this time the reconfiguration happens and the build passes), which is inconvenient for the user. Or I can try to execute cmake recursively and hope for the best (I don't think I can write this in a reliable and portable way).
- I can put all this information into a file and not CMake variables. But this is limiting and requires extra work to have files named after the revision (as it needs to parse that file). It is also hard to generate proper dependencies in this scenario, as they are not proper products of the build.
I can see two ways CMake could make this possible. A) Make a dedicated function/variable that would create the required dependency. For example, I wanted to write a function with the following signature (but failed to do so, due to the limitations described):
ConfigureDependsOnCommand(
COMMAND <cmd> [args...]
[WORKING_DIRECTORY <dir>]
[DEPENDS_ON_STDOUT]
[DEPENDS_ON_STDERR]
[DEPENDS_ON_RETVAL]
)
This would register a hook that executes given command, stores the stdout/stderr/retval in a file, and compares on each build. Doing that only for RETVAL or non-zero RETVAL would work as well, but I think the above proposal is a good compromise that is both simple and powerful enough.
B) Another option would be to provide hook to execute something before CMAKE_CONFIGURE_DEPENDS. I can then easily execute cmake in script mode and change some stamp file if required (for example, using the file(GENERATE ...) ), so that when cmake checks, it will already be updated.
I wonder why something like this isn't implemented and/or documented. Did I miss something? I don't think so, I've tried just about any script that deals with version control systems, and none of them provide the dirty flag into a variable. Looking into the ways to introduce a dependency like this lead to multitude of solutions that mostly reduced to either CMAKE_CONFIGURE_DEPENDS (because the command wasn't really necessary). In my case, I could put all the files in my repo into CMAKE_CONFIGURE_DEPENDS, but I believe that is bad practice and would probably result in significant performance hit.
Any ideas? Is there interest to implement something like this, or is there a known limitation why this can't be implemented?
I'm willing to lend a hand and give some implementation a try, if someone is willing to point me into the right direction (that would be acceptable by the cmake community).