#! /bin/sh
# Copyright (C) 2012-2018 Free Software Foundation, Inc.
#
# This program 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 2, or (at your option)
# any later version.
#
# This program 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 this program.  If not, see <https://www.gnu.org/licenses/>.

# Check support for different compression formats used by distribution
# archives.

am_create_testdir=empty
. test-init.sh

plan_ 66

# ---------------------------------------------------- #
#  Common and/or auxiliary subroutines and variables.  #
# ---------------------------------------------------- #

ocwd=$(pwd) || fatal_ "getting current working directory"

unset TAR

# Create common aclocal.m4 file, for later tests.
mkdir setup \
  && cd setup \
  && echo 'AC_INIT([x], [0]) AM_INIT_AUTOMAKE' > configure.ac \
  && $ACLOCAL \
  && mv aclocal.m4 .. \
  && cd .. \
  && rm -rf setup \
  || fatal_ "creating common aclocal.m4 file"

# Some make implementations (e.g., HP-UX) don't grok '-j', some require
# no space between '-j' and the number of jobs (e.g., older GNU make
# versions), and some *do* require a space between '-j' and the number
# of jobs (e.g., Solaris dmake).  We need a runtime test to see what
# works.
for MAKE_j4 in "$MAKE -j4" "$MAKE -j 4" false; do
  echo all: | $MAKE_j4 -f - && break
  : For shells with buggy 'set -e'.
done

# Set variables '$compressor' and '$suffix'.
setup_vars_for_compression_format ()
{
  suffix=NONE compressor=NONE
  case $1 in
    gzip) suffix=tar.gz  compressor=gzip     ;;
    lzip) suffix=tar.lz  compressor=lzip     ;;
      xz) suffix=tar.xz  compressor=xz       ;;
   bzip2) suffix=tar.bz2 compressor=bzip2    ;;
     zip) suffix=zip     compressor=zip      ;;
       *) fatal_ "invalid compression format '$1'";;
  esac
}

have_compressor ()
{
  test $# -eq 1 || fatal_ "have_compressor(): bad usage"
  if test $1 = gzip; then
    # Assume gzip(1) is available on every reasonable portability target.
    return 0
  fi
  needed_programs=$1
  # Assume by default the other compressors we care about support the
  # '--version' option.  We'll special-case the one which don't.
  checker_option=--version
  case $1 in
    bzip2)
      # Do not use --version, or older versions bzip2 would try to
      # compress stdin.  This would cause binary output in the test
      # logs, with potential breakage of our testsuite harness.
      checker_option=--help
      ;;
    zip)
      # OpenSolaris zip do not support the '--version' option, but
      # accepts the '-v' one with a similar meaning (if no further
      # arguments are given).
      checker_option=-v
      # Also, we need 'unzip' to decompress the created zipped archives
      # (bug#15181).
      needed_programs='zip unzip'
      ;;
  esac
  # Redirect to stderr to avoid polluting the output, in case this
  # function is used in a command substitution (as it is, later in
  # this script).
  for p in $needed_programs; do
    $p $checker_option </dev/null >&2 || return 1
  done
  return 0
}

all_compression_formats='gzip lzip xz bzip2 zip'

all_compressors=$(
  for x in $all_compression_formats; do
    setup_vars_for_compression_format $x
    echo $compressor
  done | tr "$nl" ' ')
echo All compressors: $all_compressors

missing_compressors=$(
  for c in $all_compressors; do
    have_compressor $c || echo $c
  done | tr "$nl" ' ')
echo Missing compressors: $missing_compressors

# Redefine to avoid re-running the already executed checks.
have_compressor ()
{
  case " $missing_compressors " in *\ $1\ *) false;; *) : ;; esac
}

have_all_compressors ()
{
  test -z "$missing_compressors"
}

start_subtest ()
{
  name=$1; shift
  test -n "$name" || fatal_ "start_subtest: no subtest name given"
  if test $# -gt 0; then
    eval "$@" || fatal_ "start_subtest: evaluating assignments"
  fi
  ac_opts=$(echo $ac_opts | tr ',' ' ')
  am_opts=$(echo $am_opts | tr ',' ' ')
  mkdir "$name"
  cd "$name"
  unindent > configure.ac <<END
    AC_INIT([$name], [1.0])
    AM_INIT_AUTOMAKE([$ac_opts])
    AC_CONFIG_FILES([Makefile])
    AC_OUTPUT
END
  echo "AUTOMAKE_OPTIONS = $am_opts" > Makefile.am
  # It is imperative that aclocal.m4 is copied after configure.ac has
  # been created, to avoid a spurious trigger of the automatic remake
  # rules for configure & co.
  cp "$ocwd"/aclocal.m4 \
     "$am_scriptdir"/missing \
     "$am_scriptdir"/install-sh \
     .
}

end_subtest ()
{
  unset name; unset ac_opts; unset am_opts;
  cd "$ocwd" || fatal_ "couldn't chdir back to '$ocwd'"
}

command_ok_if_have_compressor ()
{
  if have_compressor "$compressor"; then
    command_ok_ "$@"
  else
    skip_ -r "'$compressor' not available" "$1"
  fi
}

can_compress ()
{
  test $# -eq 2 || fatal_ "can_compress: bad number of arguments"
  tarname=$1 format=$2
  setup_vars_for_compression_format "$format"

  command_ok_ "'dist-$format' target always created" $MAKE -n dist-$format

  command_ok_if_have_compressor "'make dist-$format' work by default" \
    eval '
      rm -rf *$tarname* \
        && $MAKE dist-$format \
        && test -f $tarname-1.0.$suffix \
        && ls -l *$tarname* \
        && test "$(echo *$tarname*)" = $tarname-1.0.$suffix'

  unset suffix compressor format tarname
}

# ---------------------------------------- #
#  Defaults layout of the dist-* targets.  #
# ---------------------------------------- #

start_subtest defaults

command_ok_ "default [automake]"        $AUTOMAKE
command_ok_ "default [autoconf]"        $AUTOCONF
command_ok_ "default [configure]"       ./configure
command_ok_ "default [make distcheck]"  $MAKE distcheck

command_ok_ "'make dist' only builds *.tar.gz by default" \
            test "$(ls *defaults*)" = defaults-1.0.tar.gz

rm -rf *defaults*

for fmt in $all_compression_formats; do
  can_compress defaults $fmt
done
unset fmt

end_subtest

# ----------------------------------------------------------- #
#  Check diagnostic for no-dist-gzip without another dist-*.  #
# ----------------------------------------------------------- #

nogzip_stderr ()
{
  grep "$1:.*no-dist-gzip" stderr \
    && grep "$1:.* at least one archive format must be enabled" stderr
}

nogzip_automake_failure ()
{
  AUTOMAKE_fails -d "no-dist-gzip ($1) without other formats is an error"
  command_ok_ "no-dist-gzip ($1) without other formats gives diagnostic" \
              nogzip_stderr "$2"
}

start_subtest am-nogz-only am_opts=no-dist-gzip ac_opts=
nogzip_automake_failure 'am' 'Makefile\.am:1'
end_subtest

start_subtest ac-nogz-only am_opts= ac_opts=no-dist-gzip
nogzip_automake_failure 'ac' 'configure\.ac:2'
end_subtest

# ------------------------------------------------- #
#  Check use of no-dist-gzip with a dist-* option.  #
# ------------------------------------------------- #

append_to_opt ()
{
  var=$1_opts val=$2
  eval "$var=\${$var:+\"\$$var,\"}\$val" || fatal_ "evaluating \${$var}"
  unset var val
}

nogzip ()
{
  test $#,$1,$3,$5 = 6,in,and,in \
    && case $2,$6 in ac,ac|ac,am|am,ac|am,am) :;; *) false;; esac \
    || fatal_ "nogzip: invalid usage"
  format=$4 where_dist_nogzip=$2 where_dist_format=$6
  shift 6

  am_opts= ac_opts=
  append_to_opt $where_dist_format dist-$format
  append_to_opt $where_dist_nogzip no-dist-gzip
  setup_vars_for_compression_format "$format"
  # Do these before the am_opts and ac_opts variable can be munged
  # by 'start_subtest'.
  desc=
  test -n "$am_opts" && desc=${desc:+"$desc "}"am=$am_opts"
  test -n "$ac_opts" && desc=${desc:+"$desc "}"ac=$ac_opts"

  start_subtest nogzip-$format am_opts=$am_opts ac_opts=$ac_opts

  unindent >> Makefile.am <<END
    check-ark-name:
	test \$(DIST_ARCHIVES) = \$(distdir).$suffix
    check-ark-exists:
	test -f \$(distdir).$suffix
    check-no-tar-gz:
	test ! -f \$(distdir).tar.gz
END

  command_ok_ "$desc [automake]" $AUTOMAKE
  command_ok_ "$desc [autoconf]" $AUTOCONF
  command_ok_ "$desc [configure]" ./configure
  command_ok_ "$desc [ark-name]" $MAKE check-ark-name
  command_ok_if_have_compressor "$desc [distcheck]" $MAKE distcheck
  command_ok_if_have_compressor "$desc [ark-exists]" $MAKE check-ark-exists
  command_ok_ "$desc [no .tar.gz]"  $MAKE check-no-tar-gz

  unset desc

  end_subtest
}

#      $1 $2  $3   $4     $5  $6
nogzip in am  and  bzip2  in  am
nogzip in ac  and  xz     in  am
nogzip in am  and  lzip   in  ac
nogzip in ac  and  zip    in  ac


# ----------------------------------------------------------- #
#  The 'dist-gzip' target is created also with no-dist-gzip.  #
# ----------------------------------------------------------- #

start_subtest dist-gzip-persistence am_opts=no-dist-gzip,dist-xz
command_ok_ "dist-gzip persistence [automake]"  $AUTOMAKE
command_ok_ "dist-gzip persistence [autoconf]"  $AUTOCONF
command_ok_ "dist-gzip persistence [configure]" ./configure
can_compress dist-gzip-persistence gzip
end_subtest


# ----------------------- #
#  Parallel compression.  #
# ----------------------- #

# We only use formats requiring 'gzip', 'bzip2' and 'xz' programs,
# since there are the most likely to be all found on the majority
# of systems.

start_subtest parallel-compression ac_opts=dist-bzip2 am_opts=dist-xz

desc=gzip+bzip2+xz
tarname=parallel-compression-1.0

check_tarball ()
{
  format=$1
  setup_vars_for_compression_format $format
  (
    tarball=$tarname.$suffix \
      && test -f $tarball \
      && mkdir check-$format \
      && cp $tarball check-$format \
      && cd check-$format \
      && $compressor -d $tarball \
      && tar xvf $tarname.tar \
      && diff ../Makefile.in $tarname/Makefile.in \
      && cd .. \
      && rm -rf check-$format
   )
}

command_ok_ "$desc [automake]" $AUTOMAKE

if ! have_compressor xz && ! have_compressor bzip2; then
  skip_reason="both 'bzip2' and 'xz' are unavailable"
elif ! have_compressor xz; then
  skip_reason="'xz' not available"
elif ! have_compressor bzip2; then
  skip_reason="'bzip2' not available"
else
  skip_reason=
fi
if test "$MAKE_j4" = false; then
  test -z "$skip_reason" || skip_reason="$skip_reason, and "
  skip_reason="${skip_reason}make concurrency unavailable"
fi

if test -n "$skip_reason"; then
  skip_row_ 6 -r "$skip_reason" "$desc"
else
  command_ok_ "$desc [autoconf]" $AUTOCONF
  command_ok_ "$desc [configure]" ./configure
  command_ok_ "$desc [make -j4 dist-all]"  $MAKE_j4 dist
  ls -l # For debugging.
  command_ok_ "$desc [check .tar.gz tarball]"  check_tarball gzip
  command_ok_ "$desc [check .tar.bz2 tarball]" check_tarball bzip2
  command_ok_ "$desc [check .tar.xz tarball]"  check_tarball xz
fi

unset tarname desc skip_reason

end_subtest


# --------------------------------------------------------- #
#  The various 'dist-*' targets can happily work together.  #
# --------------------------------------------------------- #

start_subtest all-together

desc='all compressors together'
tarname=all-together-1.0

echo 'AM_INIT_AUTOMAKE([' > am-init.m4
echo 'AUTOMAKE_OPTIONS =' > Makefile.am

# Add half 'dist-*' options to AM_INIT_AUTOMAKE, half to AUTOMAKE_OPTIONS.
flip=:
for fmt in $all_compression_formats; do
  test $fmt = gzip && continue
  if $flip; then
    echo "  dist-$fmt" >> am-init.m4
    flip=false
  else
    echo "AUTOMAKE_OPTIONS += dist-$fmt" >> Makefile.am
    flip=:
  fi
done
unset flip fmt

echo '])' >> am-init.m4

sed 's/AM_INIT_AUTOMAKE.*/m4_include([am-init.m4])/' configure.ac > t
mv -f t configure.ac

# For debugging.
cat Makefile.am
cat configure.ac
cat am-init.m4

command_ok_ "$desc [aclocal]" $ACLOCAL --force
command_ok_ "$desc [automake]" $AUTOMAKE
command_ok_ "$desc [autoconf]" $AUTOCONF
command_ok_ "$desc [configure]" ./configure

if have_all_compressors; then
  command_ok_ "$desc [make distcheck, real]" $MAKE distcheck
else
  skip_ -r "not all compressors available" "$desc [make distcheck, real]"
fi

# We fake existence of all the compressors here, so that we don't have
# to require any of them to run the further tests.  This is especially
# important since it's very unlikely that a non-developer has all the
# compression tools installed on his machine at the same time.

mkdir bin
cd bin
cat > check-distdir <<END
#!/bin/sh
{ ls -l '$tarname' && diff Makefile.am '$tarname'/Makefile.am; } >&2 \
  || { echo "== distdir fail =="; exit 1; }
END
cat > grep-distdir-error <<'END'
#!/bin/sh
grep 'distdir fail' && exit 1
:
END
chmod a+x check-distdir grep-distdir-error
for prog in tar $all_compressors; do
  case $prog in
    tar|zip) cp check-distdir $prog;;
          *) cp grep-distdir-error $prog;;
  esac
done
unset prog
ls -l # For debugging.
cd ..

oPATH=$PATH
PATH=$(pwd)/bin$PATH_SEPARATOR$PATH; export PATH

command_ok_ \
  "$desc ['make dist-all', stubbed]" \
  $MAKE dist-all

subdesc="$desc ['make dist -j4', stubbed]"
if test "$MAKE_j4" = false; then
  skip_ -r "make concurrency unavailable" "$subdesc"
else
  command_ok_ "$subdesc" $MAKE_j4 dist
fi
unset subdesc

PATH=$oPATH; export PATH

end_subtest

: