Commit 2ca7dcd6 2ca7dcd648057aaa45b6b4f74acd3afbd99e410c by Sergey Poznyakoff

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.
1 parent 1da223b0
/config.rpath
*.a
*.cflow
*.la
......
......@@ -120,9 +120,8 @@ SUBDIRS = . \
$(MOVEMAIL_DIR)\
$(MIMEVIEW_DIR)
EXTRA_DIST = COPYING.LESSER paths git-describe git-describe.h
BUILT_SOURCES = git-describe git-describe.h
DISTCLEANFILES = pathdefs.h git-describe.h
EXTRA_DIST = COPYING.LESSER paths
DISTCLEANFILES = pathdefs.h
gen_start_date = "2008-12-08"
prev_change_log = "doc/ChangeLog.CVS"
......@@ -155,20 +154,7 @@ ChangeLog:
mv cl-t ChangeLog; \
fi
.PHONY: git-describe
git-describe:
$(AM_V_GEN)if test -d .git; then \
dirty=`git diff-index --name-only HEAD 2>/dev/null` || dirty=;\
test -n "$$dirty" && dirty="-dirty"; \
descr=`git describe`; \
echo $${descr}$$dirty > git-describe.tmp; \
test -f git-describe && \
cmp git-describe.tmp git-describe >/dev/null || \
cp git-describe.tmp git-describe; \
rm git-describe.tmp; \
fi
dist-hook: ChangeLog git-describe
dist-hook: ChangeLog
@PATCHLEV=`echo "$(PACKAGE_VERSION)" | \
sed -r "s/[0-9]+\.[0-9]+\.?//"`; \
if test $${PATCHLEV:-0} -lt 50; then \
......@@ -178,30 +164,20 @@ dist-hook: ChangeLog git-describe
fi; \
fi
git-describe.h: git-describe
$(AM_V_GEN)if test -f $(srcdir)/git-describe; then \
sed '1s/.*/#define GIT_DESCRIBE "&"/' $(srcdir)/git-describe; \
else \
echo "/* No git tag */"; \
fi > git-describe.h.tmp; \
test -f git-describe.h && \
cmp git-describe.h.tmp git-describe.h >/dev/null || \
cp git-describe.h.tmp git-describe.h; \
rm git-describe.h.tmp
alpha: git-describe
$(AM_V_GEN)if test -f $(srcdir)/git-describe; then \
tag=`head -n 1 $(srcdir)/git-describe`; \
alpha:
version=`$(GITINFO) -H'$$refversion{?$$n>0??-$$n?}'`;\
if test -n "$$version"; then\
$(MAKE) dist distdir=$(PACKAGE)-$$version; \
else \
tag=`date +"%Y%m%d"`; \
fi; \
$(MAKE) dist distdir=$(PACKAGE)-$(VERSION)-$$tag
$(MAKE) dist; \
fi
alphacheck:
$(AM_V_GEN)if test -f $(srcdir)/git-describe; then \
tag=`head -n 1 $(srcdir)/git-describe`; \
version=`$(GITINFO) -H'$$refversion{?$$n>0??-$$n?}'`;\
if test -n "$$version"; then\
$(MAKE) distcheck distdir=$(PACKAGE)-$$version; \
else \
tag=`date +"%Y%m%d"`; \
fi; \
$(MAKE) distcheck distdir=$(PACKAGE)-$(VERSION)-$$tag
$(MAKE) distcheck; \
fi
......
......@@ -1370,6 +1370,12 @@ CPPFLAGS="$CPPFLAGS -DSYSCONFDIR=\\\"\$(sysconfdir)\\\""
# Doc hints.
IMPRIMATUR_INIT([doc/imprimatur])
AC_PATH_PROG([PERL], perl)
if test -z "$PERL"; then
PERL=false
fi
AC_SUBST([GITINFO],'$(PERL) $(mu_aux_dir)/gitinfo.pl')
AC_CONFIG_COMMANDS([status],[
cat <<EOF
......
......@@ -15,7 +15,7 @@
## You should have received a copy of the GNU General Public License
## along with GNU Mailutils. If not, see <http://www.gnu.org/licenses/>.
BUILT_SOURCES=errno.h types.h
BUILT_SOURCES=errno.h types.h gitinfo.h
EXTRA_DIST=errno.hin types.hin
errno.h: $(top_srcdir)/libmailutils/diag/errors errno.hin
$(AM_V_GEN)$(AWK) -f $(mu_aux_dir)/generr.awk $(top_srcdir)/libmailutils/diag/errors errno.hin > errno.h
......@@ -24,7 +24,15 @@ types.h: $(top_srcdir)/include/mailutils/types.hin Makefile
$(AM_V_GEN)sed -e 's/_MU_OFF_TYPE_/$(MU_OFF_TYPE)/' \
-e 's/_MU_DEFAULT_RECORD_/$(MU_DEFAULT_RECORD)/' \
-e 's/_MU_PRI_OFF_T_/$(MU_PRI_OFF_T)/' \
$(top_srcdir)/include/mailutils/types.hin > $@
$(top_srcdir)/include/mailutils/types.hin > types.h
gitinfo.h: $(top_srcdir)/ChangeLog
$(AM_V_GEN)if ! $(GITINFO) -Hc -ogitinfo.h \
&& test ! -f $(srcdir)/gitinfo.h; then \
echo '/* no info */' > gitinfo.h;\
fi
DISTCLEANFILES = types.h
pkginclude_HEADERS = \
......@@ -51,6 +59,7 @@ pkginclude_HEADERS = \
error.h\
filter.h\
folder.h\
gitinfo.h\
glob.h\
gsasl.h\
guile.h\
......
......@@ -33,6 +33,7 @@
#include <mailutils/io.h>
#include <mailutils/syslog.h>
#include <mailutils/mu_auth.h>
#include <mailutils/gitinfo.h>
#define MU_LEGACY_CONFIG_FILE SYSCONFDIR "/mailutils.rc"
......@@ -59,10 +60,11 @@ const char mu_version_copyright[] =
void
mu_version_hook (struct mu_parseopt *po, mu_stream_t stream)
{
#ifdef GIT_DESCRIBE
mu_stream_printf (stream, "%s (%s) %s [%s]\n",
#if MU_GIT_COMMIT_DISTANCE > 0
mu_stream_printf (stream, "%s (%s) %s-%d [%s]\n",
mu_program_name, PACKAGE_NAME, PACKAGE_VERSION,
GIT_DESCRIBE);
MU_GIT_COMMIT_DISTANCE,
MU_GIT_DESCRIBE_STRING);
#else
mu_stream_printf (stream, "%s (%s) %s\n", mu_program_name,
PACKAGE_NAME, PACKAGE_VERSION);
......
......@@ -27,6 +27,7 @@
#include <mailutils/wordsplit.h>
#include <mailutils/io.h>
#include <mailutils/cli.h>
#include <mailutils/gitinfo.h>
struct getopt_data
{
......@@ -144,12 +145,13 @@ static struct mu_option folder_option[] = {
void
mh_version_hook (struct mu_parseopt *po, mu_stream_t stream)
{
#ifdef GIT_DESCRIBE
mu_stream_printf (stream, "%s (%s %s) [%s]\n",
#if MU_GIT_COMMIT_DISTANCE > 0
mu_stream_printf (stream, "%s (%s) %s-%d [%s]\n",
mu_program_name, PACKAGE_NAME, PACKAGE_VERSION,
GIT_DESCRIBE);
MU_GIT_COMMIT_DISTANCE,
MU_GIT_DESCRIBE_STRING);
#else
mu_stream_printf (stream, "%s (%s %s)\n", mu_program_name,
mu_stream_printf (stream, "%s (%s) %s\n", mu_program_name,
PACKAGE_NAME, PACKAGE_VERSION);
#endif
/* TRANSLATORS: Translate "(C)" to the copyright symbol
......
......@@ -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
......
# 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);
}
......@@ -108,6 +108,6 @@ mu-setup.h: Makefile.am $(MODULES) $(IDLE_MODULES)
$(AM_V_GEN)$(AWK) -f $(srcdir)/mu-setup.awk -v mode=h \
$(MODULES) $(IDLE_MODULES) > mu-setup.h
mu-setup.c: Makefile.am $(MODULES)
mu-setup.c: Makefile.am $(MODULES) $(IDLE_MODULES)
$(AM_V_GEN)$(AWK) -f $(srcdir)/mu-setup.awk -v mode=c \
$(MODULES) $(IDLE_MODULES) > mu-setup.c
......
......@@ -34,19 +34,10 @@ dnl
m4_define([MUT_VERSION],[
AT_SETUP([$1 version])
AT_CHECK([$1 --version | sed '1{s/ *[\[.*\]]//;q;}' ],
AT_CHECK([$1 --version | sed ['1{s/-[0-9][0-9]* //;s/ *\[.*\]//;q;}'] ],
[0],
[$1 (AT_PACKAGE_NAME) AT_PACKAGE_VERSION
],
[],
[cat >.xfailfile <<'_EOT'
==============================================================
WARNING: Not using the proper version, *all* checks dubious...
==============================================================
_EOT
],
[rm -f $[]XFAILFILE])
])
AT_CLEANUP
])
......