[RFC] Add git sha information to the gdb version string

Andrew Burgess
2020-03-25T11:48:17Z

I don't know if there is any interest in adding git sha information to
the GDB version string. I seem to recall this was discussed briefly
at 2019's GNU cauldron, but I don't recall if there was interest in
the idea or not.

Personally I think having the git information in the binary can be
helpful, so below is an initial proposal. I'm happy to change this as
needed if people have feedback on how to improve it.

Let me know what you think,

Thanks,
Andrew

---

Adds information about the precise git version from which gdb was
built. The new version string will look like this:

GNU gdb (GDB) 10.0.50.20200307-git (137acc6fbd6-72fbdf834da-2-dirty)

The first half (up to the '-git' string) is as before, and taken from
the version.in file within the gdb source tree, with the date text
taken from the bfd source tree.

The second part is new, and is built from the current git version.
This is broken down like this:

137acc6fbd6-72fbdf834da-2-dirty
| | | | | | |
'---A-----' '-----B---' C '-D-'

Where:
A - This is the last git commit sha. This represents the HEAD of the
branch that was built.
B - This is the merge base between the current branch and the
upstream master branch.
C - The number of commits from the merge base to the current sha.
This gives an impression of how diverged the branch is.
D - Is the current git tree fully committed? If not then it is dirty.

Parts B, C, and D are optional, though B and C will almost always
appear together (see below for details). The following are all valid:

137acc6fbd6-72fbdf834da-2-dirty

Current HEAD is 137acc6fbd6, which is 2 commits from the merge-base
72fbdf834da (which is in sourceware's master branch), however, the
repository that built this GDB was not fully committed, so 137acc6fbd6
will not accurately represent the source code that built this GDB.

137acc6fbd6-72fbdf834da-2

Like the above, but the repository was fully committed, so 137acc6fbd6
does exactly match the source code that built this version of GDB.

72fbdf834da-dirty

This version of GDB was built directly from sourceware's master
branch (commit 72fbdf834da), however the repository had local changes
at the time of build, so 72fbdf834da does not exactly match the source
code that built this version of GDB.

72fbdf834da

Like the above, but the repository was clean at the time of build, so
72fbdf834da exactly matches that source code that built this version
of GDB.

137acc6fbd6-unknown-dirty
137acc6fbd6-unknown

These occur when the version script can't find the remote sourceware
repository, and so is unable to figure out a suitable merge-base, nor
can the script figure out if the current HEAD is in sourceware's
master branch. In this case parts B and C are replaced with the
string 'unknown'.

I have tried to ensure that if the source code is not a git repository
then the git version token will not be added, so folks building from
tar files should get exactly what they had before.

The file gdbsupport/remote-repository contains the pattern used to
identify the remote repository, and the name of the upstream branch
from that repository which we care about. For us this will be
sourceware and master, but by moving these strings into a separate
file anyone maintaining an out of tree GDB can easily update these to
point to their repository and branch, and get project specific version
strings.

gdbsupport/ChangeLog:

* create-version.sh: Add git sha information.
* remote-repository: New file.

gdbserver/ChangeLog:

* Makefile.in (version-generated.cc): Add remote-repository file
as a dependency.

gdb/ChangeLog:

* Makefile.in (stamp-version): Add remote-repository file as a
dependency.
---
gdb/ChangeLog | 5 +++++
gdb/Makefile.in | 2 +-
gdbserver/ChangeLog | 5 +++++
gdbserver/Makefile.in | 2 +-
gdbsupport/ChangeLog | 5 +++++
gdbsupport/create-version.sh | 45 ++++++++++++++++++++++++++++++++++++++++++++
gdbsupport/remote-repository | 2 ++
7 files changed, 64 insertions(+), 2 deletions(-)
create mode 100644 gdbsupport/remote-repository

diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 0c331af4bff..9b5ad1ea176 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -2068,7 +2068,7 @@ $(srcdir)/copying.c: @MAINTAINER_MODE_TRUE@ $(srcdir)/../COPYING3 $(srcdir)/copy
version.c: stamp-version; @true
# Note that the obvious names for the temp file are taken by
# create-version.sh.
-stamp-version: Makefile version.in $(srcdir)/../bfd/version.h $(srcdir)/../gdbsupport/create-version.sh
+stamp-version: Makefile version.in $(srcdir)/../bfd/version.h $(srcdir)/../gdbsupport/create-version.sh $(srcdir)/../gdbsupport/remote-repository
$(ECHO_GEN) $(SHELL) $(srcdir)/../gdbsupport/create-version.sh $(srcdir) \
$(host_alias) $(target_alias) version-t.t
@$(SHELL) $(srcdir)/../move-if-change version-t.t version.c
diff --git a/gdbserver/Makefile.in b/gdbserver/Makefile.in
index 8c35c169d62..0c67d64b02e 100644
--- a/gdbserver/Makefile.in
+++ b/gdbserver/Makefile.in
@@ -445,7 +445,7 @@ am--refresh:

force:

-version-generated.cc: Makefile $(srcdir)/../gdb/version.in $(srcdir)/../bfd/version.h $(srcdir)/../gdbsupport/create-version.sh
+version-generated.cc: Makefile $(srcdir)/../gdb/version.in $(srcdir)/../bfd/version.h $(srcdir)/../gdbsupport/create-version.sh $(srcdir)/../gdbsupport/remote-repository
$(ECHO_GEN) $(SHELL) $(srcdir)/../gdbsupport/create-version.sh $(srcdir)/../gdb \
$(host_alias) $(target_alias) $@

diff --git a/gdbsupport/create-version.sh b/gdbsupport/create-version.sh
index 81d6dbf8c1f..2f8e5a3e283 100755
--- a/gdbsupport/create-version.sh
+++ b/gdbsupport/create-version.sh
@@ -27,9 +27,54 @@ host_alias="$2"
target_alias="$3"
output="$4"

+GIT_TAG=""
+if $(cd $srcdir && git rev-parse --show-toplevel >/dev/null 2>/dev/null); then
+ short_head_sha=$(cd $srcdir && git rev-parse --short HEAD)
+
+ dirty_mark=""
+ (cd $srcdir && git update-index -q --refresh)
+ test -z "$(cd $srcdir && git diff-index --name-only HEAD --)" ||
+ dirty_mark="-dirty"
+
+ remote_repo_pattern=`grep ^pattern: \
+ $srcdir/../gdbsupport/remote-repository \
+ | cut -d: -f2-`
+ remote_repo_branch=`grep ^branch: \
+ $srcdir/../gdbsupport/remote-repository \
+ | cut -d: -f2-`
+ branch_info=""
+ remote_name=`(cd $srcdir && git remote -v) \
+ | grep ${remote_repo_pattern} | grep \(fetch\) \
+ | awk -F '\t' '{print $1}'`
+ if [ -n "${remote_name}" ]; then
+ remote_branch="${remote_name}/${remote_repo_branch}"
+ # If the remote branch contains our commit, then we're good.
+ if ! $(cd $srcdir && git merge-base \
+ --is-ancestor ${short_head_sha} \
+ ${remote_branch}); then
+ # SHA is not on the remote tracking branch. We need to figure out
+ # the merge base, and the distance from that merge base.
+ merge_base_sha=$(cd $srcdir && git merge-base ${short_head_sha} \
+ ${remote_branch})
+ short_merge_base_sha=$(cd $srcdir \
+ && git rev-parse \
+ --short ${merge_base_sha})
+ commit_count=$(cd $srcdir \
+ && git rev-list --count ${merge_base_sha}..HEAD)
+ branch_info="-${short_merge_base_sha}-${commit_count}"
+ fi
+ else
+ branch_info="-unknown"
+ fi
+ GIT_TAG="${short_head_sha}${branch_info}${dirty_mark}"
+fi
+
rm -f version.c-tmp $output version.tmp
date=`sed -n -e 's/^.* BFD_VERSION_DATE \(.*\)$/\1/p' $srcdir/../bfd/version.h`
sed -e "s/DATE/$date/" < $srcdir/version.in > version.tmp
+if [ -n "$GIT_TAG" ]; then
+ echo " ($GIT_TAG)" >> version.tmp
+fi
echo '#include "gdbsupport/version.h"' >> version.c-tmp
echo 'const char version[] = "'"`sed q version.tmp`"'";' >> version.c-tmp
echo 'const char host_name[] = "'"$host_alias"'";' >> version.c-tmp
diff --git a/gdbsupport/remote-repository b/gdbsupport/remote-repository
new file mode 100644
index 00000000000..c05f3163e81
--- /dev/null
+++ b/gdbsupport/remote-repository
@@ -0,0 +1,2 @@
+pattern:sourceware.org/git/binutils-gdb.git
+branch:master
--
2.14.5