summaryrefslogtreecommitdiff
path: root/mu-aux
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org.ua>2017-03-20 20:57:26 +0200
committerSergey Poznyakoff <gray@gnu.org.ua>2017-03-20 22:28:47 +0200
commit2ca7dcd648057aaa45b6b4f74acd3afbd99e410c (patch)
tree1fdfa7472b738aca1fc9f20670ba839d85100c5c /mu-aux
parent1da223b05d0b851d6cb025af3d58f70e1c533d0a (diff)
downloadmailutils-2ca7dcd648057aaa45b6b4f74acd3afbd99e410c.tar.gz
mailutils-2ca7dcd648057aaa45b6b4f74acd3afbd99e410c.tar.bz2
Housekeeping: implement a tool for producing intermediate alpha tarballs.
Intermediate tarballs have the same version number as initial alpha version, to which is appended a suffix "-N", where N is the number of commits between the current git HEAD and the original alpha release. Additionally, the --version (or -version, for MH) output has been changed to include the N and the git description (for all releases, but stable). * .gitignore: Update. * Makefile.am: Remove git-describe and git-describe.h goals. (alpha, alphacheck): Rewrite. * configure.ac (GITINFO): New subst variable. * include/mailutils/Makefile.am (gitinfo.h): New built source. * libmailutils/cli/cli.c: Include gitinfo.h (mu_version_hook): Rewrite. * mh/mh_getopt.c: Include gitinfo.h (mh_version_hook): Rewrite. * mu-aux/gitinfo.pl: New file. * mu-aux/Makefile.am (EXTRA_DIST): Add gitinfo.pl * mu/Makefile.am (mu-setup.c): Add dependency * testsuite/testsuite.inc (MUT_VERSION): Account for changes in version output.
Diffstat (limited to 'mu-aux')
-rw-r--r--mu-aux/Makefile.am3
-rw-r--r--mu-aux/gitinfo.pl463
2 files changed, 465 insertions, 1 deletions
diff --git a/mu-aux/Makefile.am b/mu-aux/Makefile.am
index 4ea678cd7..99a5ad93f 100644
--- a/mu-aux/Makefile.am
+++ b/mu-aux/Makefile.am
@@ -22,7 +22,8 @@ EXTRA_DIST = \
mailutils.spec.in\
texify.sed\
sqlmod.sh\
- generr.awk
+ generr.awk\
+ gitinfo.pl
m4datadir = $(datadir)/aclocal
dist_m4data_DATA = mailutils.m4
diff --git a/mu-aux/gitinfo.pl b/mu-aux/gitinfo.pl
new file mode 100644
index 000000000..f57b962ae
--- /dev/null
+++ b/mu-aux/gitinfo.pl
@@ -0,0 +1,463 @@
+# This file is part of GNU Mailutils.
+# Copyright (C) 2017 Free Software Foundation, Inc.
+#
+# GNU Mailutils is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 3, or (at
+# your option) any later version.
+#
+# GNU Mailutils is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Mailutils. If not, see <http://www.gnu.org/licenses/>.
+
+use strict;
+use Getopt::Long qw(:config gnu_getopt no_ignore_case);
+use Pod::Usage;
+use Pod::Man;
+use Pod::Find qw(pod_where);
+
+=head1 NAME
+
+gitinfo.pl - build version tag for mailutils
+
+=head1 SYNOPSIS
+
+B<perl gitinfo.pl>
+[B<-sv>]
+[B<-C> I<DIR>]
+[B<-H> I<FORMAT>]
+[B<-o> I<FILE>]
+[B<--directory=>I<DIR>]
+[B<--format=>I<FORMAT>]
+[B<--output=>I<FILE>]
+[B<--stable>]
+[B<--verbose>]
+
+B<perl gitinfo.pl> B<-h> | B<--help> | B<--usage>
+
+=head1 DESCRIPTION
+
+Outputs Git information for the version of GNU mailutils in
+the local source tree. The information is printed according to
+the supplied I<FORMAT>. The format is an arbitrary string, containing
+variable references in the form B<$I<VARIABLE>> or B<${I<VARIABLE>}>.
+If the variable reference occurs within a quoted string, any occurrences
+of double quotes and backslashes in the expansion will be escaped by
+backslashes.
+
+The following variables are defined:
+
+=over 4
+
+=item B<package>
+
+Package name, obtained from the B<AC_INIT> line in F<configure.ac>.
+
+=item B<version>
+
+Package version, from the same source.
+
+=item B<refversion>
+
+Reference version. By default, it is the same as B<version> above. If,
+however, the B<-s> (B<--stable>) option is given, B<refversion> is set
+to the most recent stable release. The B<NEWS> file is analyzed in order
+to find it.
+
+=item B<refdate>
+
+Date when B<refversion> was released. Normally this is meaningful only for
+stable versions (in other words, when the B<-s> option is given).
+
+=item B<refcommit>
+
+Hash of the commit corresponding to the B<refversion>.
+
+=item B<n>
+
+Number of commits between B<refcommit> and B<HEAD>.
+
+=item B<describe>
+
+Result of the B<git describe> command.
+
+=item B<dirty>
+
+If the source tree has uncommitted modifications, this variable is set
+to 1. Otherwise, it is undefined.
+
+=item B<commit_hash>
+
+The hash of the topmost commit.
+
+=item B<commit_time>
+
+Time of the topmost commit (UNIX timestamp).
+
+=item B<commit_subject>
+
+Subject of the topmost commit.
+
+=back
+
+The construct
+
+B<{?I<EXPR>??I<REPL-IF-TRUE>>[B<?|I<REPL-IF-FALSE>>]B<?}>
+
+within a format string provides a rudimental control flow facility. The
+I<EXPR> is evaluated as a Perl expression in an environment when the variales
+disucussed above are defined. If the result is true (in Perl sense), the
+construct expands to I<REPL-IF-TRUE>. Othervise, it expands to
+I<REPL-IF-FALSE> or, if it is not defined, to the empty string.
+
+Notice, that conditional expressions cannot be nested.
+
+In addition to the format strings, the argument to the B<--format> option
+can be any of the following predefined format names:
+
+=over 4
+
+=item B<h>
+
+=item B<c>
+
+Produces a set of B<C> defines. It is equivalent to the following multi-line
+format:
+
+ #define MU_GIT_COMMIT_HASH "$commit_hash"
+ #define MU_GIT_COMMIT_TIME "$commit_time"
+ #define MU_GIT_COMMIT_SUBJECT "$commit_subject"
+ {?$n>0??#define MU_GIT_COMMIT_DISTANCE $n
+ ?}#define MU_GIT_DESCRIBE_STRING "$describe{?$dirty??-dirty?}"
+
+=item B<all>
+
+This is the default format. It outputs all variables as B<I<NAME>="I<VALUE>">
+pairs, each pair on a separate line. Occurrences of B<"> and B<\> within
+values are escaped by additional backslashes.
+
+=back
+
+=head1 OPTIONS
+
+=over 4
+
+=item B<-C>, B<--directory=>I<DIR>
+
+Use I<DIR> as the top-level source directory. By default it is determined
+automatically, which works for as long as B<gitinfo.pl> is run from a
+subdirectory of the git-controlled working tree.
+
+=item B<-H>, B<--format>=I<FORMAT>
+
+Select output format. See B<DESCRIPTION> for the details.
+
+=item B<-o>, B<--output=>I<FILE>
+
+Output results to the I<FILE>, instead of the standard output.
+
+=item B<-s>, B<--stable>
+
+Count versions from the most recent stable version.
+
+=item B<-v>, B<--verbose>
+
+Verbose output.
+
+=item B<-h>
+
+Display short help summary.
+
+=item B<--help>
+
+Display the manpage.
+
+=item B<--usage>
+
+Display command line usage summary.
+
+=back
+
+=cut
+
+# find_commit(VERSION)
+# Finds commit corresponding to the version number VERSION.
+sub find_commit($) {
+ my $v = quotemeta(shift);
+ my $cmd = q{git log -S'^AC_INIT\(\[[^]]+\][[:space:]]*,[[:space:]]*\[}
+ . $v
+ . q{\]' --pickaxe-regex --reverse --pretty=format:%H -- configure.ac};
+ open(my $fd, '-|', $cmd)
+ or die "$cmd: $!";
+ my $s = <$fd>;
+ close $fd;
+ chomp $s;
+ return $s;
+}
+
+# find_count(VERSIN)
+# Returns number of commits between VERSION and the current commit.
+sub find_count($) {
+ my $v = shift;
+ my $cmd = "git rev-list --count $v..HEAD";
+ open(my $fd, '-|', $cmd) or die "$cmd: $!";
+ my $s = <$fd>;
+ close $fd;
+ chomp $s;
+ return $s;
+}
+
+# recent_version(STABLE)
+# Returns the most recent version described in the NEWS file.
+# If STABLE is true, returns the most recent stable version, i.e.
+# the one, for which release date is set.
+sub recent_version($) {
+ my ($stable) = @_;
+ my $file = 'NEWS';
+ open(my $fd, '<', $file) or die "can't open $file: $!";
+ my ($version, $date);
+ while (<$fd>) {
+ chomp;
+ if (/^(?:\*[[:space:]]+)?
+ [vV]ersion [[:space:]]+
+ ([[:digit:]](?:[.,][[:digit:]]+){1,2}(?:[[:digit:]._-])*)
+ (?:(?:.*)[[:punct:]][[:space:]]*
+ ([[:digit:]]{4}-[[:digit:]]{2}-[[:digit:]]{2}))?/x) {
+ next if ($stable && !defined($2));
+ ($version, $date) = ($1, $2);
+ last;
+ }
+ }
+ close $fd;
+ return ($version, $date);
+}
+
+# this_version()
+# Returns a list (package, version).
+sub this_version() {
+ my $file = 'configure.ac';
+ open(my $fd, '<', $file) or die "can't open $file: $!";
+ while (<$fd>) {
+ chomp;
+ return ($1, $2) if (/^AC_INIT\(\[(.+?)\]\s*,\s*\[(.+?)\].*/);
+ }
+}
+
+# git_describe($HASHREF)
+# Define 'describe' and 'dirty' keys in $HASHREF
+sub git_describe($) {
+ my ($href) = @_;
+ my $descr = `git describe`;
+ chomp $descr;
+ $href->{describe} = $descr;
+ if (`git diff-index --name-only HEAD 2>/dev/null`) {
+ $href->{dirty} = 1;
+ }
+ return $descr;
+}
+
+# last_commit_info($HASHREF)
+# Populates %$HASHREF with entries describing the current commit.
+sub last_commit_info {
+ my ($hashref) = @_;
+ my @names = qw(commit_hash commit_time commit_subject);
+ open(my $fd,'-|',
+ "git log --max-count=1 --pretty=format:'%H%n%ai%n%s' HEAD")
+ or die;
+ while (<$fd>) {
+ chomp;
+ my $name = shift @names;
+ last unless $name;
+ $hashref->{$name} = $_;
+ }
+ close $fd;
+}
+
+# Convert POD markup to the usage message suitable for --help and --usage
+# output.
+sub pod_usage_msg() {
+ my %args;
+ open my $fd, '>', \my $msg;
+
+ $args{-input} = pod_where({-inc => 1}, __PACKAGE__);
+ pod2usage(-verbose => 99,
+ -sections => 'NAME',
+ -output => $fd,
+ -exitval => 'NOEXIT',
+ %args);
+ my @a = split /\n/, $msg;
+ $msg = $a[1];
+ $msg =~ s/^\s+//;
+ $msg =~ s/ - /: /;
+ return $msg;
+}
+
+my %gitinfo;
+
+sub eval_format {
+ my ($format) = @_;
+ my @res;
+ while ($format) {
+ if ($format =~ /^(?<pfx>.*?)"(?<sfx>.*)$/s) {
+ push @res, eval_format($+{pfx});
+ my $acc;
+ $format = $+{sfx};
+ my $s = $format;
+ while ($s) {
+ if ($s =~ /^([^\\"]*?)\\"(.*)$/s) {
+ $acc .= $1 . '"';
+ $s = $2;
+ } elsif ($s =~ /^(.*?)"(.*)$/s) {
+ $acc .= $1;
+ $format = $2;
+ last;
+ } else {
+ $acc = undef;
+ last;
+ }
+ }
+ if (defined($acc)) {
+ my $x = eval_format($acc);
+ $x =~ s/(["\\])/\\$1/g;
+ push @res, '"', $x, '"';
+ }
+ } elsif ($format =~ /^(?<pfx>.*?)
+ \{\?
+ (?<cond>.+?)
+ \?\?
+ (?<iftrue>.+?)
+ (?:\?\|(?<iffalse>.*?))?
+ \?\}(?<sfx>.*)$/sx) {
+
+ my ($pfx, $cond, $iftrue, $iffalse, $sfx) =
+ ($+{pfx}, $+{cond}, $+{iftrue}, $+{iffalse}, $+{sfx});
+
+ use Safe;
+ my $s = new Safe;
+ while (my ($k,$v) = each %gitinfo) {
+ ${$s->varglob($k)} = $v;
+ }
+ my $x = $s->reval($cond);
+ push @res, eval_format($pfx);
+ if ($x) {
+ push @res, eval_format($iftrue);
+ } else {
+ push @res, eval_format($iffalse);
+ }
+ $format = $sfx;
+ } elsif ($format =~ /^(?<pfx>.*?)
+ \$(?<ocb>{?)
+ (?<var>[a-zA-Z_][a-zA-Z_0-9]*)
+ (?<ccb>}?)
+ (?<sfx>.*)$/sx) {
+ my ($pfx, $ocb, $var, $ccb, $sfx) =
+ ($+{pfx}, $+{ocb}, $+{var}, $+{ccb}, $+{sfx});
+ if ("$ocb$ccb" =~ /^(?:{})?$/) {
+ push @res, $pfx;
+ my $val = $gitinfo{$var} if defined $gitinfo{$var};
+ #$val =~ s/(["\\])/\\$1/g if $odq;
+ push @res, $val;
+ $format = $sfx;
+ } else {
+ last;
+ }
+ } else {
+ last;
+ }
+ }
+
+ push @res, $format if $format;
+
+ return join('', @res);
+}
+
+my $from_stable;
+my $verbose;
+my $output;
+my $format = 'all';
+
+my %fmtab = (
+ c => <<'EOT'
+#define MU_GIT_COMMIT_HASH "$commit_hash"
+#define MU_GIT_COMMIT_TIME "$commit_time"
+#define MU_GIT_COMMIT_SUBJECT "$commit_subject"
+{?$n>0??#define MU_GIT_COMMIT_DISTANCE $n
+?}#define MU_GIT_DESCRIBE_STRING "$describe{?$dirty??-dirty?}"
+EOT
+ ,
+ 'h' => 'c',
+ 'all' => sub {
+ foreach my $name (sort keys %gitinfo) {
+ my $val = $gitinfo{$name};
+ $val =~ s/(["\\])/\\$1/g;
+ print "$name=\"$val\"\n";
+ }
+ }
+);
+
+my $dir;
+
+GetOptions("help" => sub {
+ pod2usage(-exitstatus => 0, -verbose => 2);
+ },
+ "h" => sub {
+ pod2usage(-message => pod_usage_msg(),
+ -exitstatus => 0);
+ },
+ "usage" => sub {
+ pod2usage(-exitstatus => 0, -verbose => 0);
+ },
+ "directory|C=s" => \$dir,
+ "stable|s" => \$from_stable,
+ "verbose|v" => \$verbose,
+ "format|H=s" => \$format,
+ "output|o=s" => \$output
+ ) or exit(1);
+
+if ($output) {
+ open(STDOUT, '>', $output) or die "can't open $output: $!"
+}
+
+if ($dir) {
+ chdir($dir) or die "can't change to $dir: $!";
+} elsif (! -d '.git') {
+ $dir = `git rev-parse --show-toplevel 2>/dev/null`;
+ chomp $dir;
+ chdir($dir) or die "can't change to $dir: $!";
+}
+
+if (-d '.git') {
+ ($gitinfo{refversion}, my $date) = recent_version($from_stable);
+ $gitinfo{refdate} = $date if defined $date;
+ if ($verbose) {
+ print STDERR "# counting from version $gitinfo{refversion}";
+ print STDERR ", released on $gitinfo{refdate}"
+ if exists $gitinfo{refdate};
+ print STDERR "\n";
+ }
+
+ $gitinfo{refcommit} = find_commit($gitinfo{refversion});
+ print STDERR "# commit $gitinfo{refcommit}\n"
+ if $verbose;
+ my $n = find_count($gitinfo{refcommit});
+
+ if ($n =~ /^[1-9][0-9]*$/) {
+ $gitinfo{n} = $n;
+ }
+ last_commit_info(\%gitinfo);
+ git_describe(\%gitinfo);
+ ($gitinfo{package}, $gitinfo{version}) = this_version;
+}
+
+$format = $fmtab{$format} while exists $fmtab{$format};
+if (ref($format) eq 'CODE') {
+ &{$format}
+} else {
+ $format = "$format\n" unless $format =~ /\n$/s;
+ print eval_format($format);
+}
+

Return to:

Send suggestions and report system problems to the System administrator.