much better git related version numbering in our (*nix for now) build process
Yves Orton [Wed, 31 Dec 2008 00:16:23 +0000 (01:16 +0100)]
The net result of this patch is to make available via Config.pm and -v/-V the
details about the git version info we have available for the build. When built within
a git repository git is queried directly. When built from a snapshot or bundle
it is assumed that the source is unchanged, and that the required details are
avaialble in a file called .patch, whose format current is a four field string
in the following format: "$branchname $date.$time $sha1 $describe". The
generator of these files currently resides on camel.booking.com.

* git-describe is now used more directly with -v.
When the prefix of git-describe matches the version number
as determined by the defines in patchlevel.h then we use ONLY
the git-describe output, otherwise we include
the git describe in parenthesis after the version number. Either way
the describe text is optionally followed by a star should there be
uncommitted changes.
eg: This is perl, v5.11.0 (GitLive-blead-136-g58ca560) built for i686-linux
or: This is perl, v5.11.0-1-g58ca560 built for i686-linux
or: This is perl, v5.11.0 built for i686-linux

* include the SHA1 in perl -V summary, and automatically include unpushed
commits in the registered patches list

* include various git/version/.patch details in %Config, as follows:

git_commit_id             # sha1 of HEAD
git_ancestor              # ancestor in $remote/$branch (presumably canonical)
git_describe              # git describe
git_branch                # current branch
git_uncommitted_changes   # "true" if there are any, empty otherwise
git_unpushed_commits      # List of sha1's of unpushed commits
git_commit_id_title       # Used to make the perl -V summary output

Additionally one more value is added depending on build process used: when
building from an rsynced snapshot (or any dist including a file called
.patch) then the second field will be used to populate the
"git_snapshot_date" field. Otherwise if built in a git directory (as is hopefully
recommended these day) then the field will be "git_commit_date" which will be the
commit date of HEAD.

This patch introduces two new files (on top of .patchnum) that will be generated by
make_patchnum.sh: "lib/Config_git.pl" and "unpushed.h", the former is used to make
git data available to Config.pm/%Config without rebuilding everything else, and the
second is used to expose unpushed commits (if any) via the registered patch facility
of patchlevel.h

12 files changed:
.gitignore
Makefile.SH
cflags.SH
configpm
install_lib.pl
installperl
lib/.gitignore
make_patchnum.sh
myconfig.SH
patchlevel.h
perl.c
t/run/switches.t

index 4b430f9..362fc7a 100644 (file)
@@ -5,6 +5,7 @@
 # these are generated by make_patchnum.sh from git or from a .patchfile
 .patchnum
 .sha1
+unpushed.h
 
 # ignore bug*.pl
 bug*.pl
index ff50cfd..9ad8b6f 100644 (file)
@@ -371,7 +371,10 @@ CCCMD    = `sh $(shellflags) cflags "optimize='$(OPTIMIZE)'" $@`
 
 CCCMDSRC = `sh $(shellflags) cflags "optimize='$(OPTIMIZE)'" $<`
 
+# we dont include lib/Config_git.pl here, as it causes circular dependencies
 CONFIGPM = lib/Config.pm lib/Config_heavy.pl
+# so we add it here:
+CONFIGPM_EXTRA = $(CONFIGPM) lib/Config_git.pl
 
 CONFIGPOD = lib/Config.pod
 
@@ -540,7 +543,8 @@ sperl.i: perl.c $(h)
 make_patchnum:
        sh $(shellflags) make_patchnum.sh
 
-perl$(OBJ_EXT): .patchnum .sha1
+# make sure that we recompile perl.c if .patchnum changes
+perl$(OBJ_EXT): .patchnum unpushed.h
 
 translators:   miniperl$(EXE_EXT) $(CONFIGPM) FORCE
        @echo " "; echo "       Making x2p stuff"; cd x2p; $(LDLIBPTH) $(MAKE) all
@@ -1217,7 +1221,8 @@ _tidy:
        -cd utils; $(LDLIBPTH) $(MAKE) clean
        -cd x2p; $(LDLIBPTH) $(MAKE) clean
        -rm .patchnum
-       -rm .sha1
+       -rm lib/Config_git.pl
+       -rm unpushed.h
        -@for x in $(DYNALOADER) $(dynamic_ext) $(static_ext) $(nonxs_ext) ; do \
        $(LDLIBPTH) sh ext/util/make_ext clean $$x MAKE=$(MAKE) ; \
        done
index 6e6e8a9..132f466 100755 (executable)
--- a/cflags.SH
+++ b/cflags.SH
@@ -297,12 +297,12 @@ for file do
     opmini) ;;
     pad) ;;
     perl) 
-        if [ -f .patchnum -a -n "$(cat .patchnum)" ] ; then
-                ccflags="-DPERL_PATCHNUM=`cat .patchnum` $ccflags"
-        fi
-        if [ -f .sha1 -a -n "$(cat .sha1)" ] ; then
-                ccflags="-DPERL_GIT_SHA1=`cat .sha1` $ccflags"
+        if [ -f .patchnum -a -n "$(awk 'BEGIN{ORS=""} /describe:/ {print $1}' .patchnum)" ] ; then
+                ccflags="-DPERL_PATCHNUM=$(awk 'BEGIN{ORS=""} /describe:/ {print $2}' .patchnum) $ccflags"
         fi
+       if [ -f .patchnum -a -n "$(awk 'BEGIN{ORS=""} /status:/ {print $2}' .patchnum)" ] ; then
+                ccflags="-DPERL_GIT_UNCOMMITTED_CHANGES=$(awk 'BEGIN{ORS=""} /status:/ {print $2}' .patchnum) $ccflags"
+       fi
         ;;
     perlapi) ;;
     perlmain) ;;
index d781b50..af6f65a 100755 (executable)
--- a/configpm
+++ b/configpm
@@ -458,7 +458,19 @@ my $summary_expanded;
 sub myconfig {
     return $summary_expanded if $summary_expanded;
     ($summary_expanded = $summary) =~ s{\$(\w+)}
-                { my $c = $Config::Config{$1}; defined($c) ? $c : 'undef' }ge;
+                { 
+                       my $c;
+                       if ($1 eq 'git_ancestor_line') {
+                               if ($Config::Config{git_ancestor}) {
+                                       $c= "\n  Ancestor: $Config::Config{git_ancestor}";
+                               } else {
+                                       $c= "";
+                               }
+                       } else {
+                               $c = $Config::Config{$1}; 
+                       }
+                       defined($c) ? $c : 'undef' 
+               }ge;
     $summary_expanded;
 }
 
@@ -535,6 +547,11 @@ foreach my $prefix (qw(libs libswanted)) {
 
 $heavy_txt .= "EOVIRTUAL\n";
 
+$heavy_txt .= <<'ENDOFGIT';
+require 'Config_git.pl';
+$Config_SH_expanded .= $Config::Git_Data;
+ENDOFGIT
+
 $heavy_txt .= $fetch_string;
 
 $config_txt .= <<'ENDOFEND';
index 7eeae1d..ae8ba0a 100644 (file)
@@ -29,6 +29,7 @@ OS
        undef %Config::;
        delete $INC{"Config.pm"};
        delete $INC{"Config_heavy.pl"};
+       delete $INC{"Config_git.pl"};
        # You never saw us. We weren't here.
 
        require Config;
index da8f45e..d8d36be 100755 (executable)
@@ -759,7 +759,7 @@ sub installlib {
     if ($dir =~ /^auto/ ||
          ($name =~ /^(.*)\.(?:pm|pod)$/ && $archpms{$1}) ||
          ($name =~ /^(.*)\.(?:h|lib)$/i && ($Is_W32 || $Is_NetWare)) ||
-         $name eq 'Config_heavy.pl'
+         $name=~/^Config_(heavy\|git)\.pl\z/
        ) {
        $installlib = $installarchlib;
        return unless $do_installarchlib;
index e6facd3..0fa004e 100644 (file)
@@ -4,6 +4,7 @@
 /Config.pm
 /Config.pod
 /Config_heavy.pl
+/Config_git.pl
 /DB_File.pm
 /Data
 /Devel/DProf.pm
index 9a2f946..c9c74af 100644 (file)
@@ -1,35 +1,85 @@
 #!/bin/sh
 
-. ./config.sh
+# this script is used to regenerate a number of special build files
+# based on either information contained in a file called .patch or
+# directly from git.
+# The files involved are:
+#   .patchnum         # information about the current checkout
+#   lib/Config_git.pl # holds some special configure settings related to git
+#   unpushed.h        # header file used by patchlevel.h to store unpushed commits
 
-Existing=`cat .patchnum 2>/dev/null`
-Existing_Sha1=`cat .sha1 2>/dev/null`
+existing_patchnum=$(cat .patchnum 2>/dev/null)
+existing_config=$(cat lib/Config_git.pl 2>/dev/null)
+existing_unpushed=$(cat unpushed.h 2>/dev/null)
 
-if test -s ".patch" ; then
-       Current=`awk '{print $4}' .patch`
-       Sha1=`awk '{print $3}' .patch`
-elif test -d ".git" ; then
-       # we should do something better here
-       Current=`git describe`
-       Sha1=`git rev-parse HEAD`
-       Changed=`git diff-index --name-only HEAD`
-       test -n "$Changed"  && Current="$Current-with-uncommitted-changes"
-fi
+unpushed_commits='/*no-op*/'
+if [ -s ".patch" ] ; then
+       read branch snapshot_created commit_id describe < .patch
+       changed=""
+       extra_info="git_snapshot_date='$snapshot_created'"
+       commit_title='Snapshot of:'
+elif [ -d ".git" ]; then
+       branch=$(git branch | awk 'BEGIN{ORS=""} /\*/ { print $2 }')
+       test -n "$branch" && remote=$(git config branch.$branch.remote)
+       commit_id=$(git rev-parse HEAD)
+       changed=$(git diff-index --name-only HEAD)
+       describe=$(git describe --tags)
+       commit_created=$(git log -1 --pretty='format:%ci')
+       new_patchnum="describe: $describe"
+       extra_info="git_commit_date='$commit_created'"
+       if [ -n "$branch" ] && [ -n "$remote" ]; then
+               unpushed_commit_list=$(git cherry $remote/$branch | awk 'BEGIN{ORS=","} /+/ {print $2}' | sed -e 's/,$//')
+               unpushed_commits=$(git cherry $remote/$branch | awk 'BEGIN{ORS="\t\\\n"} /+/ {print ",\"" $2 "\""}')
 
-if test "$Existing" != "$Current" -o "$Existing_Sha1" != "$Sha1" ; then
-       (echo "hi there\c" ; echo " ") >.echotmp
-       if $contains c .echotmp >/dev/null 2>&1 ; then
-               n='-n'
-               c=''
-       else
-               n=''
-               c='\c'
+               if [ -n "$unpushed_commits" ]; then
+                       commit_title="Local Commit:"
+                       ancestor=`git rev-parse $remote/$branch`
+                       extra_info="$extra_info
+git_ancestor='$ancestor'
+git_unpushed='$unpushed_commit_list'"
+               fi
+                       
+       fi
+       if [ -n "$changed" ]; then
+               changed="true"
+               commit_title="Derived from:"
+               new_patchnum="$new_patchnum
+status: uncommitted-changes"
        fi
-       rm -f .echotmp
-       echo "Updating .patchnum and .sha1"
-       echo $n "$Current$c" > .patchnum
-       echo $n "$Sha1$c" > .sha1
+       test -z "$commit_title" && commit_title='Commit id:'
+fi
 
+new_unpushed=$(cat <<EOFTEXT
+/*********************************************************************
+* WARNING: unpushed.h is automatically generated by make_patchnum.sh *
+*          DO NOT EDIT DIRECTLY - edit make_patchnum.sh instead      *
+*********************************************************************/
+#define PERL_GIT_UNPUSHED_COMMITS       $unpushed_commits
+/*leave-this-comment*/
+EOFTEXT
+)
+new_config=$(cat <<EOFDATA
+#################################################################
+# WARNING: lib/Config_git.pl is generated by make_patchnum.sh   #
+#          DO NOT EDIT DIRECTLY - edit make_patchnum.sh instead #
+#################################################################
+\$Config::Git_Data=<<'ENDOFGIT';
+git_commit_id='$commit_id'
+git_describe='$describe'
+git_branch='$branch'
+git_uncommitted_changes='$changed'
+git_commit_id_title='$commit_title'
+$extra_info
+ENDOFGIT
+EOFDATA
+)
+# only update the files if necessary, other build product depends on these files
+if [ "$existing_patchnum" != "$new_patchnum" ] || [ "$new_config" != "$existing_config" ] || [ "$existing_unpushed" != "$new_unpushed" ]; then
+       echo "Updating .patchnum and lib/Config_git.pl"
+       echo "$new_patchnum" > .patchnum
+       echo "$new_config" > lib/Config_git.pl
+       echo "$new_unpushed" > unpushed.h
 else
-       echo "Reusing .patchnum and .sha1" 
+       echo "Reusing .patchnum and lib/Config_git.pl"
 fi
+
index 3dfd953..f5b432a 100644 (file)
@@ -28,6 +28,7 @@ $startsh
 # Note that the text lines /^Summary of/ .. /^\s*$/ are copied into Config.pm.
 cat <<'!NO!SUBS!'
 Summary of my $package (revision $revision $version_patchlevel_string) configuration:
+  $git_commit_id_title $git_commit_id$git_ancestor_line
   Platform:
     osname=$osname, osvers=$osvers, archname=$archname
     uname='$myuname'
index 391cb41..a17a6e6 100644 (file)
@@ -116,9 +116,21 @@ hunk.
  */
 
 #if !defined(PERL_PATCHLEVEL_H_IMPLICIT) && !defined(LOCAL_PATCH_COUNT)
+#include "unpushed.h"
+#if !defined(PERL_GIT_UNPUSHED_COMMITS)
+#define PERL_GIT_UNPUSHED_COMMITS_PATCH /* no-op */
+#else
+#define PERL_GIT_UNPUSHED_COMMITS_PATCH PERL_GIT_UNPUSHED_COMMITS
+#endif
+#if !defined(PERL_GIT_UNCOMMITTED_CHANGES)
+#define PERL_GIT_UNCOMMITTED_CHANGES_PATCH /*no op*/
+#else
+#define PERL_GIT_UNCOMMITTED_CHANGES_PATCH , STRINGIFY(PERL_GIT_UNCOMMITTED_CHANGES)
+#endif
 static const char * const local_patches[] = {
        NULL
-       ,"DEVEL"
+       PERL_GIT_UNPUSHED_COMMITS_PATCH    /* do not remove this line */
+        PERL_GIT_UNCOMMITTED_CHANGES_PATCH /* do not remove this line */
        ,NULL
 };
 
diff --git a/perl.c b/perl.c
index 7c5da00..d224ca6 100644 (file)
--- a/perl.c
+++ b/perl.c
@@ -1904,7 +1904,6 @@ S_parse_body(pTHX_ char **env, XSINIT_t xsinit)
 #else
                    sv_catpvs(opts_prog,"\"\\nCharacteristics of this binary (from libperl): \\n");
 #endif
-                   sv_catpvs(opts_prog,"  Source revision: " STRINGIFY(PERL_GIT_SHA1) "\\n");
                    sv_catpvs(opts_prog,"  Compile-time options: $_\\n\",");
 
 #if defined(LOCAL_PATCH_COUNT)
@@ -1914,16 +1913,8 @@ S_parse_body(pTHX_ char **env, XSINIT_t xsinit)
                                 "\"  Locally applied patches:\\n\",");
                        for (i = 1; i <= LOCAL_PATCH_COUNT; i++) {
                            if (PL_localpatches[i])
-#ifdef X_PERL_PATCHNUM
-/* this is ifdef'ed out, we would enable this if we want to transform 
-   "DEVEL" registered patches into the git name */
-                               if (strEQ(PL_localpatches[i],"DEVEL"))
-                                   Perl_sv_catpvf(aTHX_ opts_prog,"q%c\t%s\n%c,",
-                                              0, STRINGIFY(PERL_PATCHNUM), 0);
-                               else
-#endif
-                                   Perl_sv_catpvf(aTHX_ opts_prog,"q%c\t%s\n%c,",
-                                              0, PL_localpatches[i], 0);
+                               Perl_sv_catpvf(aTHX_ opts_prog,"q%c\t%s\n%c,",
+                                   0, PL_localpatches[i], 0);
                        }
                    }
 #endif
@@ -3315,11 +3306,29 @@ Perl_moreswitches(pTHX_ const char *s)
        if (!sv_derived_from(PL_patchlevel, "version"))
            upg_version(PL_patchlevel, TRUE);
 #if !defined(DGUX)
-       PerlIO_printf(PerlIO_stdout(),
+       {
+           SV* level= vstringify(PL_patchlevel);
+#ifdef PERL_PATCHNUM
+           SV* num= newSVpvn(STRINGIFY(PERL_PATCHNUM),sizeof(STRINGIFY(PERL_PATCHNUM))-1);
+#ifdef PERL_GIT_UNCOMMITTED_CHANGES
+           sv_catpvf(num,"%s","*");
+#endif
+
+           if (sv_len(num)>=sv_len(level) && strnEQ(SvPV_nolen(num),SvPV_nolen(level),sv_len(level))) {
+               SvREFCNT_dec(level);
+               level= num;
+           } else {
+               sv_catpvf(level, " (%s)",SvPV_nolen(num));
+               SvREFCNT_dec(num);
+           }
+ #endif
+           PerlIO_printf(PerlIO_stdout(),
                "\nThis is perl, %"SVf
                " built for %s",
-               SVfARG(vstringify(PL_patchlevel)),
+               level,
                ARCHNAME);
+           SvREFCNT_dec(level);
+       }
 #else /* DGUX */
 /* Adjust verbose output as in the perl that ships with the DG/UX OS from EMC */
        PerlIO_printf(PerlIO_stdout(),
@@ -3332,9 +3341,6 @@ Perl_moreswitches(pTHX_ const char *s)
                        Perl_form(aTHX_ "        OS Specific Release: %s\n",
                                        OSVERS));
 #endif /* !DGUX */
-#if defined PERL_PATCHNUM
-       PerlIO_printf(PerlIO_stdout(),"\nCompiled from: %s",STRINGIFY(PERL_PATCHNUM));
-#endif
 #if defined(LOCAL_PATCH_COUNT)
        if (LOCAL_PATCH_COUNT > 0)
            PerlIO_printf(PerlIO_stdout(),
index 0d91d8a..a9e23ea 100644 (file)
@@ -260,10 +260,11 @@ SWTESTPM
 
 {
     local $TODO = '';   # these ones should work on VMS
-
+    # there are definitely known build configs where this test will fail
+    # DG/UX comes to mind. Maybe we should remove these special cases?
     my $v = sprintf "%vd", $^V;
     like( runperl( switches => ['-v'] ),
-         qr/This is perl, v$v (?:DEVEL:[-A-Za-z0-9]+ )?built for \Q$Config{archname}\E.+Copyright.+Larry Wall.+Artistic License.+GNU General Public License/s,
+         qr/This is perl, v$v(?:[-\w]+| \([^)]+\))? built for \Q$Config{archname}\E.+Copyright.+Larry Wall.+Artistic License.+GNU General Public License/s,
           '-v looks okay' );
 
 }