Merge branch 'ab/simplify-perl-makefile'
authorJunio C Hamano <gitster@pobox.com>
Tue, 13 Feb 2018 21:39:03 +0000 (13:39 -0800)
committerJunio C Hamano <gitster@pobox.com>
Tue, 13 Feb 2018 21:39:03 +0000 (13:39 -0800)
The build procedure for perl/ part has been greatly simplified by
weaning ourselves off of MakeMaker.

* ab/simplify-perl-makefile:
perl: treat PERLLIB_EXTRA as an extra path again
perl: avoid *.pmc and fix Error.pm further
Makefile: replace perl/Makefile.PL with simple make rules

1  2 
Makefile
git-send-email.perl
perl/Git.pm
t/perf/aggregate.perl
t/test-lib.sh
diff --combined Makefile
index 1a9b23b6793f91072760affefac3851ca457a2f5,409e8f6ec9b6a41333e64a0295c01cf14d364197..37e02cec1b614097b418e73f2d07bf7e830ed3c1
+++ b/Makefile
@@@ -23,6 -23,7 +23,6 @@@ all:
  # it at all).
  #
  # Define NO_OPENSSL environment variable if you do not have OpenSSL.
 -# This also implies BLK_SHA1.
  #
  # Define USE_LIBPCRE if you have and want to use libpcre. Various
  # commands such as log and grep offer runtime options to use
  #
  # Define PERL_PATH to the path of your Perl binary (usually /usr/bin/perl).
  #
- # Define NO_PERL_MAKEMAKER if you cannot use Makefiles generated by perl's
- # MakeMaker (e.g. using ActiveState under Cygwin).
- #
  # Define NO_PERL if you do not want Perl scripts or libraries at all.
  #
  # Define PYTHON_PATH to the path of your Python binary (often /usr/bin/python
  #
  # to say "export LESS=FRX (and LV=-c) if the environment variable
  # LESS (and LV) is not set, respectively".
 +#
 +# Define TEST_SHELL_PATH if you want to use a shell besides SHELL_PATH for
 +# running the test scripts (e.g., bash has better support for "set -x"
 +# tracing).
 +#
 +# When cross-compiling, define HOST_CPU as the canonical name of the CPU on
 +# which the built Git will run (for instance "x86_64").
  
  GIT-VERSION-FILE: FORCE
        @$(SHELL_PATH) ./GIT-VERSION-GEN
@@@ -479,6 -470,7 +476,7 @@@ gitexecdir = libexec/git-cor
  mergetoolsdir = $(gitexecdir)/mergetools
  sharedir = $(prefix)/share
  gitwebdir = $(sharedir)/gitweb
+ perllibdir = $(sharedir)/perl5
  localedir = $(sharedir)/locale
  template_dir = share/git-core/templates
  htmldir = $(prefix)/share/doc/git-doc
@@@ -492,7 -484,7 +490,7 @@@ mandir_relative = $(patsubst $(prefix)/
  infodir_relative = $(patsubst $(prefix)/%,%,$(infodir))
  htmldir_relative = $(patsubst $(prefix)/%,%,$(htmldir))
  
- export prefix bindir sharedir sysconfdir gitwebdir localedir
+ export prefix bindir sharedir sysconfdir gitwebdir perllibdir localedir
  
  CC = cc
  AR = ar
@@@ -657,7 -649,6 +655,7 @@@ TEST_PROGRAMS_NEED_X += test-dump-cache
  TEST_PROGRAMS_NEED_X += test-dump-fsmonitor
  TEST_PROGRAMS_NEED_X += test-dump-split-index
  TEST_PROGRAMS_NEED_X += test-dump-untracked-cache
 +TEST_PROGRAMS_NEED_X += test-example-decorate
  TEST_PROGRAMS_NEED_X += test-fake-ssh
  TEST_PROGRAMS_NEED_X += test-genrandom
  TEST_PROGRAMS_NEED_X += test-hashmap
@@@ -736,8 -727,6 +734,8 @@@ endi
  export PERL_PATH
  export PYTHON_PATH
  
 +TEST_SHELL_PATH = $(SHELL_PATH)
 +
  LIB_FILE = libgit.a
  XDIFF_LIB = xdiff/lib.a
  VCSSVN_LIB = vcs-svn/lib.a
@@@ -768,7 -757,6 +766,7 @@@ LIB_OBJS += branch.
  LIB_OBJS += bulk-checkin.o
  LIB_OBJS += bundle.o
  LIB_OBJS += cache-tree.o
 +LIB_OBJS += checkout.o
  LIB_OBJS += color.o
  LIB_OBJS += column.o
  LIB_OBJS += combine-diff.o
@@@ -820,8 -808,6 +818,8 @@@ LIB_OBJS += levenshtein.
  LIB_OBJS += line-log.o
  LIB_OBJS += line-range.o
  LIB_OBJS += list-objects.o
 +LIB_OBJS += list-objects-filter.o
 +LIB_OBJS += list-objects-filter-options.o
  LIB_OBJS += ll-merge.o
  LIB_OBJS += lockfile.o
  LIB_OBJS += log-tree.o
@@@ -861,7 -847,6 +859,7 @@@ LIB_OBJS += pretty.
  LIB_OBJS += prio-queue.o
  LIB_OBJS += progress.o
  LIB_OBJS += prompt.o
 +LIB_OBJS += protocol.o
  LIB_OBJS += quote.o
  LIB_OBJS += reachable.o
  LIB_OBJS += read-cache.o
  BROKEN_PATH_FIX = '/^\# @@BROKEN_PATH_FIX@@$$/d'
  endif
  
 +ifeq (,$(HOST_CPU))
 +      BASIC_CFLAGS += -DGIT_HOST_CPU="\"$(firstword $(subst -, ,$(uname_M)))\""
 +else
 +      BASIC_CFLAGS += -DGIT_HOST_CPU="\"$(HOST_CPU)\""
 +endif
 +
  ifneq (,$(INLINE))
        BASIC_CFLAGS += -Dinline=$(INLINE)
  endif
@@@ -1274,6 -1253,7 +1272,6 @@@ ifndef NO_OPENSS
        endif
  else
        BASIC_CFLAGS += -DNO_OPENSSL
 -      BLK_SHA1 = 1
        OPENSSL_LIBSSL =
  endif
  ifdef NO_OPENSSL
@@@ -1543,9 -1523,6 +1541,6 @@@ ifdef SHA1_MAX_BLOCK_SIZ
        LIB_OBJS += compat/sha1-chunked.o
        BASIC_CFLAGS += -DSHA1_MAX_BLOCK_SIZE="$(SHA1_MAX_BLOCK_SIZE)"
  endif
- ifdef NO_PERL_MAKEMAKER
-       export NO_PERL_MAKEMAKER
- endif
  ifdef NO_HSTRERROR
        COMPAT_CFLAGS += -DNO_HSTRERROR
        COMPAT_OBJS += compat/hstrerror.o
@@@ -1732,8 -1709,10 +1727,10 @@@ ETC_GITATTRIBUTES_SQ = $(subst ','\'',$
  DESTDIR_SQ = $(subst ','\'',$(DESTDIR))
  bindir_SQ = $(subst ','\'',$(bindir))
  bindir_relative_SQ = $(subst ','\'',$(bindir_relative))
+ mandir_SQ = $(subst ','\'',$(mandir))
  mandir_relative_SQ = $(subst ','\'',$(mandir_relative))
  infodir_relative_SQ = $(subst ','\'',$(infodir_relative))
+ perllibdir_SQ = $(subst ','\'',$(perllibdir))
  localedir_SQ = $(subst ','\'',$(localedir))
  gitexecdir_SQ = $(subst ','\'',$(gitexecdir))
  template_dir_SQ = $(subst ','\'',$(template_dir))
@@@ -1742,7 -1721,6 +1739,7 @@@ prefix_SQ = $(subst ','\'',$(prefix)
  gitwebdir_SQ = $(subst ','\'',$(gitwebdir))
  
  SHELL_PATH_SQ = $(subst ','\'',$(SHELL_PATH))
 +TEST_SHELL_PATH_SQ = $(subst ','\'',$(TEST_SHELL_PATH))
  PERL_PATH_SQ = $(subst ','\'',$(PERL_PATH))
  PYTHON_PATH_SQ = $(subst ','\'',$(PYTHON_PATH))
  TCLTK_PATH_SQ = $(subst ','\'',$(TCLTK_PATH))
@@@ -1843,9 -1821,6 +1840,6 @@@ all:
  ifndef NO_TCLTK
        $(QUIET_SUBDIR0)git-gui $(QUIET_SUBDIR1) gitexecdir='$(gitexec_instdir_SQ)' all
        $(QUIET_SUBDIR0)gitk-git $(QUIET_SUBDIR1) all
- endif
- ifndef NO_PERL
-       $(QUIET_SUBDIR0)perl $(QUIET_SUBDIR1) PERL_PATH='$(PERL_PATH_SQ)' prefix='$(prefix_SQ)' localedir='$(localedir_SQ)' all
  endif
        $(QUIET_SUBDIR0)templates $(QUIET_SUBDIR1) SHELL_PATH='$(SHELL_PATH_SQ)' PERL_PATH='$(PERL_PATH_SQ)'
  
@@@ -1911,9 -1886,7 +1905,9 @@@ builtin/help.sp builtin/help.s builtin/
  version.sp version.s version.o: GIT-VERSION-FILE GIT-USER-AGENT
  version.sp version.s version.o: EXTRA_CPPFLAGS = \
        '-DGIT_VERSION="$(GIT_VERSION)"' \
 -      '-DGIT_USER_AGENT=$(GIT_USER_AGENT_CQ_SQ)'
 +      '-DGIT_USER_AGENT=$(GIT_USER_AGENT_CQ_SQ)' \
 +      '-DGIT_BUILT_FROM_COMMIT="$(shell GIT_CEILING_DIRECTORIES=\"$(CURDIR)/..\" \
 +              git rev-parse -q --verify HEAD || :)"'
  
  $(BUILT_INS): git$X
        $(QUIET_BUILT_IN)$(RM) $@ && \
@@@ -1928,7 -1901,8 +1922,8 @@@ common-cmds.h: $(wildcard Documentation
  
  SCRIPT_DEFINES = $(SHELL_PATH_SQ):$(DIFF_SQ):$(GIT_VERSION):\
        $(localedir_SQ):$(NO_CURL):$(USE_GETTEXT_SCHEME):$(SANE_TOOL_PATH_SQ):\
-       $(gitwebdir_SQ):$(PERL_PATH_SQ):$(SANE_TEXT_GREP):$(PAGER_ENV)
+       $(gitwebdir_SQ):$(PERL_PATH_SQ):$(SANE_TEXT_GREP):$(PAGER_ENV):\
+       $(perllibdir_SQ)
  define cmd_munge_script
  $(RM) $@ $@+ && \
  sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \
@@@ -1972,23 -1946,12 +1967,12 @@@ git.res: git.rc GIT-VERSION-FIL
  $(SCRIPT_PERL_GEN): GIT-BUILD-OPTIONS
  
  ifndef NO_PERL
- $(SCRIPT_PERL_GEN): perl/perl.mak
- perl/perl.mak: perl/PM.stamp
- perl/PM.stamp: FORCE
-       @$(FIND) perl -type f -name '*.pm' | sort >$@+ && \
-       $(PERL_PATH) -V >>$@+ && \
-       { cmp $@+ $@ >/dev/null 2>/dev/null || mv $@+ $@; } && \
-       $(RM) $@+
- perl/perl.mak: GIT-CFLAGS GIT-PREFIX perl/Makefile perl/Makefile.PL
-       $(QUIET_SUBDIR0)perl $(QUIET_SUBDIR1) PERL_PATH='$(PERL_PATH_SQ)' prefix='$(prefix_SQ)' $(@F)
+ $(SCRIPT_PERL_GEN):
  
- PERL_DEFINES = $(PERL_PATH_SQ):$(PERLLIB_EXTRA_SQ)
- $(SCRIPT_PERL_GEN): % : %.perl perl/perl.mak GIT-PERL-DEFINES GIT-VERSION-FILE
+ PERL_DEFINES = $(PERL_PATH_SQ):$(PERLLIB_EXTRA_SQ):$(perllibdir_SQ)
+ $(SCRIPT_PERL_GEN): % : %.perl GIT-PERL-DEFINES GIT-VERSION-FILE
        $(QUIET_GEN)$(RM) $@ $@+ && \
-       INSTLIBDIR=`MAKEFLAGS= $(MAKE) -C perl -s --no-print-directory instlibdir` && \
+       INSTLIBDIR='$(perllibdir_SQ)' && \
        INSTLIBDIR_EXTRA='$(PERLLIB_EXTRA_SQ)' && \
        INSTLIBDIR="$$INSTLIBDIR$${INSTLIBDIR_EXTRA:+:$$INSTLIBDIR_EXTRA}" && \
        sed -e '1{' \
@@@ -2312,6 -2275,21 +2296,21 @@@ endi
  po/build/locale/%/LC_MESSAGES/git.mo: po/%.po
        $(QUIET_MSGFMT)mkdir -p $(dir $@) && $(MSGFMT) -o $@ $<
  
+ LIB_PERL := $(wildcard perl/Git.pm perl/Git/*.pm perl/Git/*/*.pm perl/Git/*/*/*.pm)
+ LIB_PERL_GEN := $(patsubst perl/%.pm,perl/build/lib/%.pm,$(LIB_PERL))
+ ifndef NO_PERL
+ all:: $(LIB_PERL_GEN)
+ endif
+ perl/build/lib/%.pm: perl/%.pm
+       $(QUIET_GEN)mkdir -p $(dir $@) && \
+       sed -e 's|@@LOCALEDIR@@|$(localedir_SQ)|g' < $< > $@
+ perl/build/man/man3/Git.3pm: perl/Git.pm
+       $(QUIET_GEN)mkdir -p $(dir $@) && \
+       pod2man $< $@
  FIND_SOURCE_FILES = ( \
        git ls-files \
                '*.[hcS]' \
@@@ -2375,7 -2353,6 +2374,7 @@@ GIT-LDFLAGS: FORC
  # and the first level quoting from the shell that runs "echo".
  GIT-BUILD-OPTIONS: FORCE
        @echo SHELL_PATH=\''$(subst ','\'',$(SHELL_PATH_SQ))'\' >$@+
 +      @echo TEST_SHELL_PATH=\''$(subst ','\'',$(TEST_SHELL_PATH_SQ))'\' >>$@+
        @echo PERL_PATH=\''$(subst ','\'',$(PERL_PATH_SQ))'\' >>$@+
        @echo DIFF=\''$(subst ','\'',$(subst ','\'',$(DIFF)))'\' >>$@+
        @echo PYTHON_PATH=\''$(subst ','\'',$(PYTHON_PATH_SQ))'\' >>$@+
@@@ -2572,7 -2549,9 +2571,9 @@@ ifndef NO_GETTEX
        (cd '$(DESTDIR_SQ)$(localedir_SQ)' && umask 022 && $(TAR) xof -)
  endif
  ifndef NO_PERL
-       $(MAKE) -C perl prefix='$(prefix_SQ)' DESTDIR='$(DESTDIR_SQ)' install
+       $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perllibdir_SQ)'
+       (cd perl/build/lib && $(TAR) cf - .) | \
+       (cd '$(DESTDIR_SQ)$(perllibdir_SQ)' && umask 022 && $(TAR) xof -)
        $(MAKE) -C gitweb install
  endif
  ifndef NO_TCLTK
@@@ -2622,12 -2601,17 +2623,17 @@@ endi
  install-gitweb:
        $(MAKE) -C gitweb install
  
- install-doc:
+ install-doc: install-man-perl
        $(MAKE) -C Documentation install
  
- install-man:
+ install-man: install-man-perl
        $(MAKE) -C Documentation install-man
  
+ install-man-perl: perl/build/man/man3/Git.3pm
+       $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(mandir_SQ)/man3'
+       (cd perl/build/man/man3 && $(TAR) cf - .) | \
+       (cd '$(DESTDIR_SQ)$(mandir_SQ)/man3' && umask 022 && $(TAR) xof -)
  install-html:
        $(MAKE) -C Documentation install-html
  
@@@ -2719,7 -2703,7 +2725,7 @@@ clean: profile-clean coverage-clea
        $(MAKE) -C Documentation/ clean
  ifndef NO_PERL
        $(MAKE) -C gitweb clean
-       $(MAKE) -C perl clean
+       $(RM) -r perl/build/
  endif
        $(MAKE) -C templates/ clean
        $(MAKE) -C t/ clean
diff --combined git-send-email.perl
index 340b5c848294f6d58329b282fe5df72b8f5bbeed,88a0edcd7dd9de6e2a81cc8438e8d3a1c2e2f1df..bbf4deaa0d42ef2a843c2240000d7e34dfff0b62
@@@ -26,11 -26,10 +26,11 @@@ use Text::ParseWords
  use Term::ANSIColor;
  use File::Temp qw/ tempdir tempfile /;
  use File::Spec::Functions qw(catdir catfile);
- use Error qw(:try);
+ use Git::Error qw(:try);
  use Cwd qw(abs_path cwd);
  use Git;
  use Git::I18N;
 +use Git::Mail::Address;
  
  Getopt::Long::Configure qw/ pass_through /;
  
@@@ -490,7 -489,7 +490,7 @@@ my ($repoauthor, $repocommitter)
  ($repocommitter) = Git::ident_person(@repo, 'committer');
  
  sub parse_address_line {
 -      return Git::parse_mailboxes($_[0]);
 +      return map { $_->format } Mail::Address->parse($_[0]);
  }
  
  sub split_addrs {
@@@ -886,9 -885,7 +886,9 @@@ if (defined $initial_reply_to) 
  }
  
  if (!defined $smtp_server) {
 -      foreach (qw( /usr/sbin/sendmail /usr/lib/sendmail )) {
 +      my @sendmail_paths = qw( /usr/sbin/sendmail /usr/lib/sendmail );
 +      push @sendmail_paths, map {"$_/sendmail"} split /:/, $ENV{PATH};
 +      foreach (@sendmail_paths) {
                if (-x $_) {
                        $smtp_server = $_;
                        last;
diff --combined perl/Git.pm
index 65e6b32a0f6401bd749ea26700a3f59939db2aa5,02a3871e9405bf2b0b881955e19188a16bd31b1a..9d60d7948b22254e6f61cc0d984b4ef40f27bc4f
@@@ -101,7 -101,7 +101,7 @@@ increase notwithstanding)
  
  
  use Carp qw(carp croak); # but croak is bad - throw instead
- use Error qw(:try);
+ use Git::Error qw(:try);
  use Cwd qw(abs_path cwd);
  use IPC::Open2 qw(open2);
  use Fcntl qw(SEEK_SET SEEK_CUR);
@@@ -880,6 -880,77 +880,6 @@@ sub ident_person 
        return "$ident[0] <$ident[1]>";
  }
  
 -=item parse_mailboxes
 -
 -Return an array of mailboxes extracted from a string.
 -
 -=cut
 -
 -# Very close to Mail::Address's parser, but we still have minor
 -# differences in some cases (see t9000 for examples).
 -sub parse_mailboxes {
 -      my $re_comment = qr/\((?:[^)]*)\)/;
 -      my $re_quote = qr/"(?:[^\"\\]|\\.)*"/;
 -      my $re_word = qr/(?:[^]["\s()<>:;@\\,.]|\\.)+/;
 -
 -      # divide the string in tokens of the above form
 -      my $re_token = qr/(?:$re_quote|$re_word|$re_comment|\S)/;
 -      my @tokens = map { $_ =~ /\s*($re_token)\s*/g } @_;
 -      my $end_of_addr_seen = 0;
 -
 -      # add a delimiter to simplify treatment for the last mailbox
 -      push @tokens, ",";
 -
 -      my (@addr_list, @phrase, @address, @comment, @buffer) = ();
 -      foreach my $token (@tokens) {
 -              if ($token =~ /^[,;]$/) {
 -                      # if buffer still contains undeterminated strings
 -                      # append it at the end of @address or @phrase
 -                      if ($end_of_addr_seen) {
 -                              push @phrase, @buffer;
 -                      } else {
 -                              push @address, @buffer;
 -                      }
 -
 -                      my $str_phrase = join ' ', @phrase;
 -                      my $str_address = join '', @address;
 -                      my $str_comment = join ' ', @comment;
 -
 -                      # quote are necessary if phrase contains
 -                      # special characters
 -                      if ($str_phrase =~ /[][()<>:;@\\,.\000-\037\177]/) {
 -                              $str_phrase =~ s/(^|[^\\])"/$1/g;
 -                              $str_phrase = qq["$str_phrase"];
 -                      }
 -
 -                      # add "<>" around the address if necessary
 -                      if ($str_address ne "" && $str_phrase ne "") {
 -                              $str_address = qq[<$str_address>];
 -                      }
 -
 -                      my $str_mailbox = "$str_phrase $str_address $str_comment";
 -                      $str_mailbox =~ s/^\s*|\s*$//g;
 -                      push @addr_list, $str_mailbox if ($str_mailbox);
 -
 -                      @phrase = @address = @comment = @buffer = ();
 -                      $end_of_addr_seen = 0;
 -              } elsif ($token =~ /^\(/) {
 -                      push @comment, $token;
 -              } elsif ($token eq "<") {
 -                      push @phrase, (splice @address), (splice @buffer);
 -              } elsif ($token eq ">") {
 -                      $end_of_addr_seen = 1;
 -                      push @address, (splice @buffer);
 -              } elsif ($token eq "@" && !$end_of_addr_seen) {
 -                      push @address, (splice @buffer), "@";
 -              } else {
 -                      push @buffer, $token;
 -              }
 -      }
 -
 -      return @addr_list;
 -}
 -
  =item hash_object ( TYPE, FILENAME )
  
  Compute the SHA1 object id of the given C<FILENAME> considering it is
diff --combined t/perf/aggregate.perl
index 5c439f6bc23e782e09924c02abe3d4edc4cd0d03,923044df6a0ace8870c83eee5ce1de6fba682b28..3a0917fa61ef6e630ca4dc2bb28c2af671f98804
@@@ -1,9 -1,8 +1,9 @@@
  #!/usr/bin/perl
  
- use lib '../../perl/blib/lib';
+ use lib '../../perl/build/lib';
  use strict;
  use warnings;
 +use JSON;
  use Git;
  
  sub get_times {
@@@ -36,15 -35,10 +36,15 @@@ sub format_times 
        return $out;
  }
  
 -my (@dirs, %dirnames, %dirabbrevs, %prefixes, @tests);
 +my (@dirs, %dirnames, %dirabbrevs, %prefixes, @tests, $codespeed);
  while (scalar @ARGV) {
        my $arg = $ARGV[0];
        my $dir;
 +      if ($arg eq "--codespeed") {
 +              $codespeed = 1;
 +              shift @ARGV;
 +              next;
 +      }
        last if -f $arg or $arg eq "--";
        if (! -d $arg) {
                my $rev = Git::command_oneline(qw(rev-parse --verify), $arg);
@@@ -75,19 -69,12 +75,19 @@@ if (not @tests) 
        @tests = glob "p????-*.sh";
  }
  
 +my $resultsdir = "test-results";
 +my $results_section = "";
 +if (exists $ENV{GIT_PERF_SUBSECTION} and $ENV{GIT_PERF_SUBSECTION} ne "") {
 +      $resultsdir .= "/" . $ENV{GIT_PERF_SUBSECTION};
 +      $results_section = $ENV{GIT_PERF_SUBSECTION};
 +}
 +
  my @subtests;
  my %shorttests;
  for my $t (@tests) {
        $t =~ s{(?:.*/)?(p(\d+)-[^/]+)\.sh$}{$1} or die "bad test name: $t";
        my $n = $2;
 -      my $fname = "test-results/$t.subtests";
 +      my $fname = "$resultsdir/$t.subtests";
        open my $fp, "<", $fname or die "cannot open $fname: $!";
        for (<$fp>) {
                chomp;
@@@ -108,6 -95,13 +108,6 @@@ sub read_descr 
        return $line;
  }
  
 -my %descrs;
 -my $descrlen = 4; # "Test"
 -for my $t (@subtests) {
 -      $descrs{$t} = $shorttests{$t}.": ".read_descr("test-results/$t.descr");
 -      $descrlen = length $descrs{$t} if length $descrs{$t}>$descrlen;
 -}
 -
  sub have_duplicate {
        my %seen;
        for (@_) {
@@@ -123,117 -117,54 +123,117 @@@ sub have_slash 
        return 0;
  }
  
 -my %newdirabbrevs = %dirabbrevs;
 -while (!have_duplicate(values %newdirabbrevs)) {
 -      %dirabbrevs = %newdirabbrevs;
 -      last if !have_slash(values %dirabbrevs);
 -      %newdirabbrevs = %dirabbrevs;
 -      for (values %newdirabbrevs) {
 -              s{^[^/]*/}{};
 +sub print_default_results {
 +      my %descrs;
 +      my $descrlen = 4; # "Test"
 +      for my $t (@subtests) {
 +              $descrs{$t} = $shorttests{$t}.": ".read_descr("$resultsdir/$t.descr");
 +              $descrlen = length $descrs{$t} if length $descrs{$t}>$descrlen;
        }
 -}
  
 -my %times;
 -my @colwidth = ((0)x@dirs);
 -for my $i (0..$#dirs) {
 -      my $d = $dirs[$i];
 -      my $w = length (exists $dirabbrevs{$d} ? $dirabbrevs{$d} : $dirnames{$d});
 -      $colwidth[$i] = $w if $w > $colwidth[$i];
 -}
 -for my $t (@subtests) {
 -      my $firstr;
 +      my %newdirabbrevs = %dirabbrevs;
 +      while (!have_duplicate(values %newdirabbrevs)) {
 +              %dirabbrevs = %newdirabbrevs;
 +              last if !have_slash(values %dirabbrevs);
 +              %newdirabbrevs = %dirabbrevs;
 +              for (values %newdirabbrevs) {
 +                      s{^[^/]*/}{};
 +              }
 +      }
 +
 +      my %times;
 +      my @colwidth = ((0)x@dirs);
        for my $i (0..$#dirs) {
                my $d = $dirs[$i];
 -              $times{$prefixes{$d}.$t} = [get_times("test-results/$prefixes{$d}$t.times")];
 -              my ($r,$u,$s) = @{$times{$prefixes{$d}.$t}};
 -              my $w = length format_times($r,$u,$s,$firstr);
 +              my $w = length (exists $dirabbrevs{$d} ? $dirabbrevs{$d} : $dirnames{$d});
                $colwidth[$i] = $w if $w > $colwidth[$i];
 -              $firstr = $r unless defined $firstr;
        }
 -}
 -my $totalwidth = 3*@dirs+$descrlen;
 -$totalwidth += $_ for (@colwidth);
 -
 -binmode STDOUT, ":utf8" or die "PANIC on binmode: $!";
 +      for my $t (@subtests) {
 +              my $firstr;
 +              for my $i (0..$#dirs) {
 +                      my $d = $dirs[$i];
 +                      $times{$prefixes{$d}.$t} = [get_times("$resultsdir/$prefixes{$d}$t.times")];
 +                      my ($r,$u,$s) = @{$times{$prefixes{$d}.$t}};
 +                      my $w = length format_times($r,$u,$s,$firstr);
 +                      $colwidth[$i] = $w if $w > $colwidth[$i];
 +                      $firstr = $r unless defined $firstr;
 +              }
 +      }
 +      my $totalwidth = 3*@dirs+$descrlen;
 +      $totalwidth += $_ for (@colwidth);
  
 -printf "%-${descrlen}s", "Test";
 -for my $i (0..$#dirs) {
 -      my $d = $dirs[$i];
 -      printf "   %-$colwidth[$i]s", (exists $dirabbrevs{$d} ? $dirabbrevs{$d} : $dirnames{$d});
 -}
 -print "\n";
 -print "-"x$totalwidth, "\n";
 -for my $t (@subtests) {
 -      printf "%-${descrlen}s", $descrs{$t};
 -      my $firstr;
 +      printf "%-${descrlen}s", "Test";
        for my $i (0..$#dirs) {
                my $d = $dirs[$i];
 -              my ($r,$u,$s) = @{$times{$prefixes{$d}.$t}};
 -              printf "   %-$colwidth[$i]s", format_times($r,$u,$s,$firstr);
 -              $firstr = $r unless defined $firstr;
 +              printf "   %-$colwidth[$i]s", (exists $dirabbrevs{$d} ? $dirabbrevs{$d} : $dirnames{$d});
        }
        print "\n";
 +      print "-"x$totalwidth, "\n";
 +      for my $t (@subtests) {
 +              printf "%-${descrlen}s", $descrs{$t};
 +              my $firstr;
 +              for my $i (0..$#dirs) {
 +                      my $d = $dirs[$i];
 +                      my ($r,$u,$s) = @{$times{$prefixes{$d}.$t}};
 +                      printf "   %-$colwidth[$i]s", format_times($r,$u,$s,$firstr);
 +                      $firstr = $r unless defined $firstr;
 +              }
 +              print "\n";
 +      }
 +}
 +
 +sub print_codespeed_results {
 +      my ($results_section) = @_;
 +
 +      my $project = "Git";
 +
 +      my $executable = `uname -s -m`;
 +      chomp $executable;
 +
 +      if ($results_section ne "") {
 +              $executable .= ", " . $results_section;
 +      }
 +
 +      my $environment;
 +      if (exists $ENV{GIT_PERF_REPO_NAME} and $ENV{GIT_PERF_REPO_NAME} ne "") {
 +              $environment = $ENV{GIT_PERF_REPO_NAME};
 +      } elsif (exists $ENV{GIT_TEST_INSTALLED} and $ENV{GIT_TEST_INSTALLED} ne "") {
 +              $environment = $ENV{GIT_TEST_INSTALLED};
 +              $environment =~ s|/bin-wrappers$||;
 +      } else {
 +              $environment = `uname -r`;
 +              chomp $environment;
 +      }
 +
 +      my @data;
 +
 +      for my $t (@subtests) {
 +              for my $d (@dirs) {
 +                      my $commitid = $prefixes{$d};
 +                      $commitid =~ s/^build_//;
 +                      $commitid =~ s/\.$//;
 +                      my ($result_value, $u, $s) = get_times("$resultsdir/$prefixes{$d}$t.times");
 +
 +                      my %vals = (
 +                              "commitid" => $commitid,
 +                              "project" => $project,
 +                              "branch" => $dirnames{$d},
 +                              "executable" => $executable,
 +                              "benchmark" => $shorttests{$t} . " " . read_descr("$resultsdir/$t.descr"),
 +                              "environment" => $environment,
 +                              "result_value" => $result_value,
 +                          );
 +                      push @data, \%vals;
 +              }
 +      }
 +
 +      print to_json(\@data, {utf8 => 1, pretty => 1}), "\n";
 +}
 +
 +binmode STDOUT, ":utf8" or die "PANIC on binmode: $!";
 +
 +if ($codespeed) {
 +      print_codespeed_results($results_section);
 +} else {
 +      print_default_results();
  }
diff --combined t/test-lib.sh
index 9a0a21f49ae7bef3d0a1b653cce811d12499672e,85a0fabb471645b05ab3bd2c44f5b5c774c7de09..9af19055b307e2c8306efbd41a89338ee408b838
@@@ -80,7 -80,7 +80,7 @@@ done,*
        # from any previous runs.
        >"$GIT_TEST_TEE_OUTPUT_FILE"
  
 -      (GIT_TEST_TEE_STARTED=done ${SHELL_PATH} "$0" "$@" 2>&1;
 +      (GIT_TEST_TEE_STARTED=done ${TEST_SHELL_PATH} "$0" "$@" 2>&1;
         echo $? >"$BASE.exit") | tee -a "$GIT_TEST_TEE_OUTPUT_FILE"
        test "$(cat "$BASE.exit")" = 0
        exit
@@@ -264,6 -264,7 +264,6 @@@ d
                shift ;;
        -x)
                trace=t
 -              verbose=t
                shift ;;
        --verbose-log)
                verbose_log=t
@@@ -282,11 -283,6 +282,11 @@@ the
        test -z "$verbose_log" && verbose=t
  fi
  
 +if test -n "$trace" && test -z "$verbose_log"
 +then
 +      verbose=t
 +fi
 +
  if test -n "$color"
  then
        # Save the color control sequences now rather than run tput
@@@ -590,9 -586,7 +590,9 @@@ maybe_setup_valgrind () 
  }
  
  want_trace () {
 -      test "$trace" = t && test "$verbose" = t
 +      test "$trace" = t && {
 +              test "$verbose" = t || test "$verbose_log" = t
 +      }
  }
  
  # This is a separate function because some tests use
@@@ -607,40 -601,26 +607,40 @@@ test_eval_inner_ () 
  }
  
  test_eval_ () {
 -      # We run this block with stderr redirected to avoid extra cruft
 -      # during a "-x" trace. Once in "set -x" mode, we cannot prevent
 +      # If "-x" tracing is in effect, then we want to avoid polluting stderr
 +      # with non-test commands. But once in "set -x" mode, we cannot prevent
        # the shell from printing the "set +x" to turn it off (nor the saving
        # of $? before that). But we can make sure that the output goes to
        # /dev/null.
        #
 -      # The test itself is run with stderr put back to &4 (so either to
 -      # /dev/null, or to the original stderr if --verbose was used).
 +      # There are a few subtleties here:
 +      #
 +      #   - we have to redirect descriptor 4 in addition to 2, to cover
 +      #     BASH_XTRACEFD
 +      #
 +      #   - the actual eval has to come before the redirection block (since
 +      #     it needs to see descriptor 4 to set up its stderr)
 +      #
 +      #   - likewise, any error message we print must be outside the block to
 +      #     access descriptor 4
 +      #
 +      #   - checking $? has to come immediately after the eval, but it must
 +      #     be _inside_ the block to avoid polluting the "set -x" output
 +      #
 +
 +      test_eval_inner_ "$@" </dev/null >&3 2>&4
        {
 -              test_eval_inner_ "$@" </dev/null >&3 2>&4
                test_eval_ret_=$?
                if want_trace
                then
                        set +x
 -                      if test "$test_eval_ret_" != 0
 -                      then
 -                              say_color error >&4 "error: last command exited with \$?=$test_eval_ret_"
 -                      fi
                fi
 -      } 2>/dev/null
 +      } 2>/dev/null 4>&2
 +
 +      if test "$test_eval_ret_" != 0 && want_trace
 +      then
 +              say_color error >&4 "error: last command exited with \$?=$test_eval_ret_"
 +      fi
        return $test_eval_ret_
  }
  
@@@ -939,7 -919,7 +939,7 @@@ the
        fi
  fi
  
- GITPERLLIB="$GIT_BUILD_DIR"/perl/blib/lib:"$GIT_BUILD_DIR"/perl/blib/arch/auto/Git
+ GITPERLLIB="$GIT_BUILD_DIR"/perl/build/lib
  export GITPERLLIB
  test -d "$GIT_BUILD_DIR"/templates/blt || {
        error "You haven't built things yet, have you?"
@@@ -1048,8 -1028,6 +1048,8 @@@ test -z "$NO_PERL" && test_set_prereq P
  test -z "$NO_PTHREADS" && test_set_prereq PTHREADS
  test -z "$NO_PYTHON" && test_set_prereq PYTHON
  test -n "$USE_LIBPCRE1$USE_LIBPCRE2" && test_set_prereq PCRE
 +test -n "$USE_LIBPCRE1" && test_set_prereq LIBPCRE1
 +test -n "$USE_LIBPCRE2" && test_set_prereq LIBPCRE2
  test -z "$NO_GETTEXT" && test_set_prereq GETTEXT
  
  # Can we rely on git's output in the C locale?