11

When using Autoconf in a project managed with Subversion, I would put this code in configure.ac:

AC_REVISION($Revision: 1234 $)

With svn:keywords Revision, AC_REVISION would insert the revision number of configure.ac into the generated configure script.

How can I do something similar in a project managed with Git?

Git doesn't have keywords like $Revision$, and doesn't have revision numbers as such. But it does have SHA1s for commits, and git describe. I'm just not sure how to incorporate that into configure.ac.

AlG
  • 14,697
  • 4
  • 41
  • 54
cjm
  • 61,471
  • 9
  • 126
  • 175
  • If you're using a unix-like system, you could write a git-hook to perform a `sed -i 's/\$Revision/$REVISION/g' configure.ac` (just an example). The `$REVISION` var may contain the result of `git describe` if you will. Sounds plausible? Otherwise, you might use `$Id:$`, which will be replaced by the sha1 of the blob (not the commit). See [this question] (http://stackoverflow.com/questions/384108/moving-from-cvs-to-git-id-equivalent). – jweyrich Nov 21 '11 at 17:39
  • 1
    Also read [why this is not a good idea](http://stackoverflow.com/questions/384108/moving-from-cvs-to-git-id-equivalent/384640#384640) when using a distributed SCM. – jweyrich Nov 21 '11 at 17:45
  • 1
    See http://stackoverflow.com/questions/3593003/injecting-mercurial-changeset-as-version-information-in-a-c-executable/3607158 – William Pursell Nov 21 '11 at 20:07
  • @jweyrich, `AC_REVISION` is an internal-only version string (i.e. it appears in the source code of the generated `configure` script, but isn't displayed to the user). There's nothing wrong with using the commit's SHA1 for that. – cjm Mar 13 '13 at 06:08

6 Answers6

6

You can actually execute any command with M4 when Autoconf runs. Thus maybe you want something like:

AC_REVISION([m4_esyscmd_s([git describe --always])])

Note that unlike with $Revision$ strings your configure.ac will not change every time you update your tree. Hence configure will not get regenerated after each update and the revision put into configure will simply be the last version for which configure was generated.

adl
  • 15,627
  • 6
  • 51
  • 65
5

adl's answer wasn't quite what I wanted, but it pointed me in the right direction. Here's what I came up with:

Put this in configure.ac:

AC_REVISION([m4_esyscmd([./tools/configure.commit])])

Save this as tools/configure.commit (and make it executable):

#! /bin/sh
# Display the SHA1 of the commit in which configure.ac was last modified.
# If it's not checked in yet, use the SHA1 of HEAD plus -dirty.

if [ ! -d .git ] ; then
  # if no .git directory, assume they're not using Git
  printf 'unknown commit'
elif git diff --quiet HEAD -- configure.ac ; then
  # configure.ac is not modified
  printf 'commit %s' `git rev-list --max-count=1 HEAD -- configure.ac`
else # configure.ac is modified
  printf 'commit %s-dirty' `git rev-parse HEAD`
fi

That combination will put the SHA-1 of the commit in which configure.ac was last modified into configure, which is what I was looking for. But there's a problem. Git doesn't touch the modification time of files when it commits them. This means that configure will continue to contain the OLDSHA-dirty value instead of being updated, because autoconf won't realize that it's out of date.

You can solve that with a post-commit hook. Save this as .git/hooks/post-commit (and make sure you chmod it as executable, or it won't run):

#!/bin/sh
#
# Copy this to .git/hooks/post-commit

# If configure.ac was just checked in, touch it,
# so that configure will be regenerated and
# AC_REVISION will reflect the new commit.
#
# For some reason, --quiet isn't actually quiet,
# so redirect output to /dev/null

git diff-tree --quiet HEAD -- configure.ac >/dev/null \
 || touch -c configure.ac
Community
  • 1
  • 1
cjm
  • 61,471
  • 9
  • 126
  • 175
2

The established autoconf solution for git is using build-aux/git-version-gen, see eg. https://github.com/kergoth/autoconf/blob/master/build-aux/git-version-gen

and then you just need you ensure that your tags are using the same prefix (i.e. v) as the version.

AC_INIT([GNU project], m4_esyscmd([build-aux/git-version-gen .tarball-version]), [bug-project@example])

This works with git and non-git releases, where .version is tracking the .git dev version, and .tarball-version the non-git release version.

rurban
  • 4,025
  • 24
  • 27
  • I can only get this to work on the first go ... on a clean checkout it works great but I can't find a consistent way to get it updated on each commit. Even touching configure.ac and manually re-running `./configure` doesn't update. – Caleb Jan 10 '20 at 07:58
  • 1
    Should I commit a copy of `git-version-gen` into my repository, or should I add some kind of build time dependency that provides this file? – lanoxx Jan 18 '20 at 21:02
1

I was trying to achieve something similar as the OP; I wanted to embed Git commit-id in Postgres version string. The code in Postgres' configure.in, in the same line that I intended to modify, already had an example.

The gist of it is that you can embed shell snippet in string literals in configure.in, and the resulting configure file (the shell executing the shell script, actually) will always execute that shell snippet to build the resultant string.

Please see the patch. Following are the patches to configure.in and relevant section from resulting configure file.

AC_DEFINE_UNQUOTED(PG_VERSION_STR,
-                   ["PostgreSQL $PACKAGE_VERSION on $host, compiled by $cc_string, `expr $ac_cv_sizeof_void_p \* 8`-bit"],
+                   ["PostgreSQL $PACKAGE_VERSION (commit `cd $srcdir && git log -1 --format=format:%h`) on $host, compiled by $cc_string, `expr $ac_cv_sizeof_void_p \* 8`-bit"],
                    [A string containing the version number, platform, and C compiler])

Resulting configure code:

 cat >>confdefs.h <<_ACEOF
-#define PG_VERSION_STR "PostgreSQL $PACKAGE_VERSION on $host, compiled by $cc_string, `expr $ac_cv_sizeof_void_p \* 8`-bit"
+#define PG_VERSION_STR "PostgreSQL $PACKAGE_VERSION (commit `cd $srcdir && git log -1 --format=format:%h`) on $host, compiled by $cc_string, `expr $ac_cv_sizeof_void_p \* 8`-bit"
 _ACEOF

Postgres version string before and after the patch:

PostgreSQL 9.3.0 on x86_64-unknown-linux-gnu, compiled by  ...
PostgreSQL 9.3.0 (commit 2cf9dac) on x86_64-unknown-linux-gnu, compiled by ...
Gurjeet Singh
  • 2,635
  • 2
  • 27
  • 22
  • The big problem with this patch is that you can no longer `make dist`, because your `configure` script assumes it's running inside a Git repo. If somebody unpacks a tarball and runs `./configure`, they'll wind up with a weird looking version string. – cjm Nov 03 '13 at 18:14
  • The general approach can work, but you need more complex shell code that handles the "not a Git repo" case. I suggest having `make dist` generate a GITREVISION file in the tarball, and reading the commit from that if `$srcdir/.git` doesn't exist. – cjm Nov 03 '13 at 18:18
  • On a side note, this isn't solving quite the same problem. I'm trying to record the version of the `configure` script, and you're trying to record the version of the code being configured. That's why my solution runs at `autoconf` time, and yours runs at `configure` time. – cjm Nov 03 '13 at 18:34
  • I agree that this embedded script needs more love to cover all cases (dirty tree, unavailability of Git repository info, etc.). Note that in absence of .git directory in current directory. Git commands traverse up directory tree until they find one; so if project code is extracted in, say, a Git-managed $HOME, then `git log` will return info of that upper directory (another case my mini-patch fails to address, and it's not a problem in your proposal). I wanted to document it, in form of this patch, so that people can use this technique if it works for them. – Gurjeet Singh Nov 05 '13 at 08:00
0

Git has something similar, but you have to specifically enable it for the relevant paths through the .gitattributes file.

   ident
       When the attribute ident is set for a path, git replaces $Id$ in
       the blob object with $Id:, followed by the 40-character hexadecimal
       blob object name, followed by a dollar sign $ upon checkout. Any
       byte sequence that begins with $Id: and ends with $ in the worktree
       file is replaced with $Id$ upon check-in.
jamessan
  • 41,569
  • 8
  • 85
  • 85
  • 1
    I'm not sure how useful the SHA1 of the blob would be. Given the blob's SHA1, how do you find the commit that includes it? I'd prefer something like `git describe`. – cjm Nov 21 '11 at 19:04
0
Community
  • 1
  • 1
Lazy Badger
  • 94,711
  • 9
  • 78
  • 110