install_p4perl.sh #5

  • //
  • guest/
  • perforce_software/
  • p4perl/
  • main/
  • install_p4perl.sh
  • View
  • Commits
  • Open Download .zip Download (15 KB)
#!/bin/bash
#------------------------------------------------------------------------------
set -u

#==============================================================================
# Internal Functions

#------------------------------------------------------------------------------
# Function msg ($message)
#
# Input:
#  Arg 1, $message, message to display.
#
# Sample Usage:
# msg "This is a good message."
#------------------------------------------------------------------------------
function msg () { echo -e "$*"; }

#------------------------------------------------------------------------------
# Function bail ($message, [$exitCode])
#
# Input:
#  Arg 1, $message, error message to display.
#  Arg 2, $exitCode, exit status. Default is 1.
#
# Sample Usage:
# bail "Something went wrong"
#------------------------------------------------------------------------------
function bail () {
   msg "Error: ${1:-Unknown Error}\\n"
   msg "Aborting after $(($SECONDS/3600)) hours $(($SECONDS%3600/60)) minutes $(($SECONDS%60)) seconds.\\n"

   exit "${2:-1}"
}

#------------------------------------------------------------------------------
# Function cmd ($command, [$desc], $[log], [$showLog])
#
# Input:
#  Arg 1, $command, the command to run.
#  Arg 2, $description, optional description to show before running the
#  command
#  Arg 3, $log, name of file to log to, e.g. 'make.log'.  Can be relative
#  or absolute path.
#  Arg 4, $showLog: Determines whether to cat the output of $log. Values:
#  0=never, 1=always, 2=on non-zero exit of command only.
#  The default is 1 (always).
#
# Usage Samples:
# cmd "ls" "Listing directory" || bail "Failed to list dir." 
# cmd "make test" "Building and Testing" make_test.log 2
#------------------------------------------------------------------------------
function cmd () {
   declare command="${1:-echo}"
   declare desc="${2:-}"
   declare log="${3:-}"
   declare showLog="${4:-1}"
   declare -i exitCode

   [[ -n "${desc:-}" ]] && msg "$desc"

   if [[ $NO_OP -eq 0 ]]; then
      if [[ -n "$log" ]]; then
         msg "Executing: $command, logging to $log."
         $command > $log 2>&1
         exitCode=$?
         if [[ $showLog -ne 0 ]]; then
            if [[ $showLog -eq 1 ]]; then
               msg "Output of $log:\n"
               cat $log
            elif [[ $exitCode -ne 0 ]]; then
               msg "Command failed.  Output of $log:\n"
               cat $log
            fi
         fi
      else
         msg "Executing: $command"
         $command
         exitCode=$?
      fi
   else
      msg "NO_OP: Would run: $command"
      exitCode=0
   fi
   return $exitCode
}

#------------------------------------------------------------------------------
# Function: terminate
function terminate
{
   # Disable signal trapping.
   trap - EXIT SIGINT SIGTERM

   # Stop logging.
   [[ "${LogFile}" == off ]] || msg "LogFile is $LogFile\n${H1}\n"

   # With the trap removed, exit.
   exit $OverallReturnStatus
}

#------------------------------------------------------------------------------
# Function: usage (required function)
#
# Input:
# $1 - style, either -h (for short form) or -man (for man-page like format).
# The default is -h.
#
# $2 - error message (optional).  Specify this if usage() is called due to
# user error, in which case the given message displayed first, followed by the
# standard usage message (short or long depending on $1).  If displaying an
# errror, usually $1 should be -h so that the longer usage message doesn't
# obsure the error message.
#
# Sample Usage:
# usage
# usage -h
# usage -man
# usage -h "Incorrect command line usage."
#------------------------------------------------------------------------------
function usage
{
   declare style=${1:--h}
   declare errorMessage=${2:-Unset}

   if [[ $errorMessage != Unset ]]; then
      echo -e "\\n\\nUsage Error:\\n\\n$errorMessage\\n\\n"
   fi

   echo "USAGE for $ThisScript v$Version:

$ThisScript [-e <perl_root> | -r <perl_root> [-p <perl_rel>] [-d <perl_config_defs>]] [-a <api_rel>] [-C] [-f] [-t] [-s] [-L <log>] [-n] [-D]

or

$ThisScript [-h|-man]
"
   if [[ $style == -man ]]; then
      echo -e "
DESCRIPTION:
	This script downloads Perl source code, the Perforce C++ API in binary
	form, and P4Perl source code. It builds, tests, and installs Perl, and
	then builds, tests, and installs P4Perl.

	Optionally, if the '-e /path/to/my/perl' flag is used, an existing Perl
	installation is updated with P4Perl. In that case, P4Perl source code
	and the Perforce C++ API in binary form are downloaded, and only P4Perl
	is built and installed.

OPTIONS:
 -e <path_to_existing_perl_root>
	Specify the path to the install root of an existing existing Perl
	installation. This directory will typically include bin, man, lib,
	and possibly other directories.

	The '-e' option cannot be used with '-r' or '-p'.

 -r	Specify the Perl install root.  The default is $InstallRoot.
	The user running this script may require sudo priviledges depending
	on the install root used.

	Install will be attempted without sudo first.  If the install fails,
	it will be attempted a second time with sudo (unless already running
	as root).

	Cannot be used with '-e'.

 -p	Specify the Perl version, e.g. 5.12.4.  The default is $PerlRel.

	Cannot be used with '-e'.

 -d	Specify Perl configure definitions to pass to the Configure utility.
	If there are more than one, the should be a quoted, space-delimited
	list.  Sample value: -d '-Dusethreads'.  This would translate into
	this Perl Configure command:
        ./Configure -des -Dprefix=/usr/local/perl -Dusethreads

 -a	Specify the Perforce API to use.  The default is $P4APIRel.

 -C	Specify -C to remove the working directory, including the
	downloads directory. This can be used if ensure a clean start
	point of multiple iterations are needed to achieve a successful
	build.

 -f	Force thru, ignoring failed tests.  By default, if test suites for
	Perl or P4Perl fail, this script aborts, making it harder (by
	design) to accidentally ignore the test failures.  In some cases,
	test failures may be deemed safely ignorable; this flag is for
	those scenarios.

 -t	Specify '-t' to execute Perl tests.  By default Perl tests
	are not executed, as they are time-consuming and possibly
	redundant if run on modern common platforms.

	P4Perl tests are always executed, though those execute quickly.

 -s	Specify '-s' to build and install Perl only, skipping P4Perl
	installation.

 -L <log>
	Specify the path to a log file, or the special value 'off' to disable
	logging.  By default, all output (stdout and stderr) goes to:
	${LogFile}.

 -n	No-Op.  Prints commands instead of running them.

 -D     Set extreme debugging verbosity.

HELP OPTIONS:
 -h	Display short help message
 -man	Display man-style help message

REQUIREMENTS:
	This script requires a public internet connection with 'http:' access
	to ftp.perforce.com and www.cpan.org.

	It requires the 'curl' utility, the g++ compiler, and 'make'.

EXAMPLES:
	Example 1: Typical usage, building a local Perl/P4Perl in
	/home/perforce/perl, using Perl v$PerlRel, building P4Perl from the
	current directory, and using the $P4APIRel release of the Perforce C++ API:
	$ThisScript -r /home/perforce/perl

	Example 2: Build only Perl, v5.12.4, for testing, skipping P4Perl build:
	$ThisScript -r /tmp/test_perl_5.12.4 -p 5.12.4 -s
"
   fi

   exit 1
}

#==============================================================================
# Declarations
declare InstallRoot="/usr/local/perl"
declare WorkingDir="/tmp/p4perl"
declare DownloadsDir=
declare PerlRel="5.28.1"
declare PerlTarFile=
declare PerlBuildDir=
declare PerlURL=
declare P4APIRel="r18.2"
declare Platform=
declare P4APIURL=
declare P4APITarFile="p4api.tgz"
declare P4APIDir=
declare P4PerlBuildDir="$PWD"
declare ThisScript=${0##*/}
declare CommandLine="$ThisScript $*"
declare LogFile="/tmp/$ThisScript.$(date +'%Y%m%d-%H%M%S').log"
declare H1="==============================================================================="
declare H2="-------------------------------------------------------------------------------"
declare PerlConfigDefs=
declare -i IgnoreFailedTests=0
declare -i RunPerlTests=0
declare -i UseExistingPerl=0
declare -i SkipP4Perl=0
declare -i CleanWorkingDir=0
export NO_OP=0
declare Version="1.1.5"

#==============================================================================
# Command Line Processing

declare -i shiftArgs=0

set +u
while [[ $# -gt 0 ]]; do
   case $1 in
      (-r) InstallRoot="$2"; shiftArgs=1;;
      (-e) InstallRoot="$2"; UseExistingPerl=1; shiftArgs=1;;
      (-p) PerlRel="$2"; shiftArgs=1;;
      (-d) PerlConfigDefs="$2"; shiftArgs=1;;
      (-t) RunPerlTests=1;;
      (-f) IgnoreFailedTests=1;;
      (-a) P4APIRel="$2"; shiftArgs=1;;
      (-C) CleanWorkingDir=1;;
      (-s) SkipP4Perl=1;;
      (-h) usage -h;;
      (-man) usage -man;;
      (-n) export NO_OP=1;;
      (-D) set -x;; # Debug; use 'set -x' mode.
      (*) usage -h "Unknown arg ($1).";;
   esac

   # Shift (modify $#) the appropriate number of times.
   shift; while [[ $shiftArgs -gt 0 ]]; do
      [[ $# -eq 0 ]] && usage -h "Incorrect number of arguments."
      shiftArgs=$shiftArgs-1
      shift
   done
done
set -u

#==============================================================================
# Main Program

trap terminate EXIT SIGINT SIGTERM

declare -i OverallReturnStatus=0

if [[ "${LogFile}" != off ]]; then
   touch ${LogFile} || bail "Couldn't touch log file [${LogFile}]."

   [[ "${LogFile}" == off ]] || msg "${H1}\\nLogFile is $LogFile\\n"

   # Redirect stdout and stderr to a log file.
   exec > >(tee ${LogFile})
   exec 2>&1

fi

msg "${H1}\nStarting ${0##*/} v$Version at $(date).\n"

msg "Started with this command line:\n\t$CommandLine\n"

if [[ $UseExistingPerl -eq 1 ]]; then
   PerlTarFile=
   PerlURL=
else
   PerlTarFile="perl-${PerlRel}.tar.gz"
   PerlURL="http://www.cpan.org/src/5.0/$PerlTarFile"
fi

if [[ $SkipP4Perl -eq 0 ]]; then
   case "$(uname)" in
      (Linux)
         Platform="linux26x86"
         [[ "$(uname -m)" == *"_64" ]] && Platform="${Platform}_64"
      ;;
      (Darwin)
         Platform="darwin90x86"
         [[ "$(uname -m)" == *"_64" ]] && Platform="${Platform}_64"
      ;;
      (*)
         bail "Current OS/platform is not supported: $(uname -a)."
      ;;
   esac

   msg "Target platform is: $Platform."

   P4APIURL="http://ftp.perforce.com/perforce/$P4APIRel/bin.$Platform/$P4APITarFile"
else
   P4APITarFile=
fi

DownloadsDir="${WorkingDir}/downloads"

if [[ -d "$WorkingDir" ]]; then
   if [[ $CleanWorkingDir -eq 0 ]]; then
      msg "Using existing working dir: $WorkingDir"
   else
      cmd "/bin/rm -rf $WorkingDir" "Removing existing working dir due to -C." ||\
         bail "Failed to remove working dir $WorkingDir."
   fi
else
   mkdir -p "$WorkingDir" || bail "Failed to create working dir $WorkingDir"
fi

if [[ ! -d "$DownloadsDir" ]]; then
   mkdir -p "$DownloadsDir" || bail "Failed to create downloads dir $DownloadsDir."
fi

cd "$DownloadsDir" || bail "Could not cd to downloads dir $DownloadsDir."

msg "${H1}\nOperating in downloads dir [$DownloadsDir]."

if [[ $UseExistingPerl -eq 0 ]]; then
   cmd "curl -s -O $PerlURL" "Downloading Perl source code." ||\
      bail "Failed to download Perl from URL $PerlURL."
fi

if [[ $SkipP4Perl -eq 0 ]]; then
   cmd "curl -s -O $P4APIURL" "Downloading Perforce C++ API." ||\
      bail "Failed to download Perforce C++ API from URL $P4APIURL."
fi

cd "$WorkingDir" || bail "Could not cd to working dir $WorkingDir."
msg "${H1}\nOperating in working dir [$WorkingDir]."

for tarfile in $PerlTarFile $P4APITarFile; do
   cmd "tar -xzf $DownloadsDir/$tarfile" "Extracting $tarfile." ||\
      bail "Failed to extract tar file $tarfile."
done

if [[ $UseExistingPerl -eq 0 ]]; then
   PerlBuildDir="$PWD/perl-$PerlRel"

   cd $PerlBuildDir || bail "Could not cd to Perl build dir $PerlBuildDir."
   msg "${H1}\nOperating in Perl build dir [$PerlBuildDir]."

   cmd "./Configure -des -Dprefix=$InstallRoot $PerlConfigDefs" "${H2}\nConfiguring Perl." configure.log ||\
      bail "Failed to configure Perl."

   cmd "make" "${H2}\nMaking Perl" make.log || bail "Failed to build Perl."

   if [[ "$RunPerlTests" -eq 1 ]]; then
      cmd "make test" "${H2}\nTesting Perl" make_test.log
      if [[ $? -eq 0 ]]; then
         msg "Verified: Perl Test Suite passed."
      else
         if [[ $IgnoreFailedTests -eq 1 ]]; then
            msg "Warning: Perl Test Suite was not entirely successful. Ignoring due to -f."
         else
            bail "Perl Test Suite failed."
         fi
      fi
   else
      msg "Skipping 'make test' for Perl; use '-t' to run them."
   fi

   cmd "make install" "${H2}\nInstalling Perl." make_install.log
   if [[ $? -ne 0 ]]; then
      if [[ $(id -u) -eq 0 ]]; then
         bail "Install for Perl failed running as root."
      else
         msg "Install for Perl failed as $(whoami).  Trying again with sudo."
         cmd "sudo make install" "\nInstalling Perl using sudo." make_install_sudo.log ||\
            bail "Failed to install Perl with sudo to $InstallRoot."
      fi
   fi
fi

if [[ $SkipP4Perl -eq 0 ]]; then

   P4APIDir="$(ls -d $WorkingDir/p4api-*)"

   cd $P4PerlBuildDir || "Could not cd to P4Perl build dir $P4PerlBuildDir."
   msg "${H1}\nOperating in P4Perl build dir [$P4PerlBuildDir]."

   cmd "$InstallRoot/bin/perl Makefile.PL --apidir $P4APIDir" \
      "Generating Makefile for P4Perl." gen_makefile.log ||\
      bail "Failed to generate Makefile for P4Perl."

   cmd "make" "${H2}\nBuilding P4Perl" make.log ||\
      bail "Failed to build P4Perl."

   # The P4Perl test do some DVCS things, which fail if DVCS has been used
   # to clone P4Perl itself from The Workshop.  Do some environment isolation
   # to avoid shell environment contamination.
   export P4ENVIRO=/dev/null/.p4enviro
   export P4CONFIG=.p4config.local.p4perl.test
   cmd "make test" "${H2}\nTesting P4Perl" make_test.log
   if [[ $? -eq 0 ]]; then
      msg "Verified: P4Perl Test Suite passed."
   else
      if [[ $IgnoreFailedTests -eq 1 ]]; then
         msg "Warning: P4Perl Test Suite was not entirely successful. Ignoring due to -f."
      else
         bail "P4Perl Test Suite failed."
      fi
   fi

   cmd "make install" "${H2}\nInstalling P4Perl." make_install.log
   if [[ $? -ne 0 ]]; then
      if [[ $(id -u) -eq 0 ]]; then
         bail "Install for P4Perl failed running as root."
      else
         msg "Install for P4Perl failed as $(whoami).  Trying again with sudo."
         cmd "sudo make install" "\nInstalling P4Perl using sudo." make_install_sudo.log ||\
            bail "Failed to install P4Perl with sudo to $InstallRoot."
      fi
   fi

   if [[ $NO_OP -eq 0 ]]; then
      msg "Smoke Testing P4Perl with command:\n$InstallRoot/bin/perl -MP4 -e \"print P4::Identify()\""
      $InstallRoot/bin/perl -MP4 -e "print P4::Identify()"
      if [[ $? -eq 0 ]]; then
         msg "\nSmoke test was successful!\n\nPerl scripts should use this shebang line:\n#!$InstallRoot/bin/perl -w\n\nOptionally, prepend PATH with $InstallRoot/bin, and prepend MANPATH with $InstallRoot/man\n"
      else
         OverallReturnStatus=1
      fi
   fi
fi

if [[ $OverallReturnStatus -eq 0 ]]; then
   msg "${H1}\nAll processing completed successfully.\n"
else
   msg "${H1}\nProcessing completed, but with errors.  Scan above output carefully.\n" 
fi

# Illustrate using $SECONDS to display runtime of a script.
msg "That took $(($SECONDS/3600)) hours $(($SECONDS%3600/60)) minutes $(($SECONDS%60)) seconds.\n"

# Change User Description Committed
#5 25298 C. Thomas Tyler Added environment isolation to P4Perl install script so
tests that use DVCS features aren't contaminated by DVCS
usage outside the test (by scripts or humans).
#4 25296 C. Thomas Tyler Updated usage info.
Changed to skip long-running Perl tests by default; added -t to run them.
Fixed issue building from the current directory.
Enhanced to show log file at begging of output.
#3 25295 C. Thomas Tyler Simplified to operate in the current directory.

Eliminated unused logic to pull P4Perl code from an path in The
Workshop that is deprecated.
#2 25294 C. Thomas Tyler Copy/merged from another soon-to-be-deleted branch.
#1 22476 C. Thomas Tyler Updated to keep the new //p4perl/main/... and
//guest/perforce_software/p4perl/main/... in sync.

We temporarily have two mainlines for P4Perl until the
Workshop Swarm can be targeted to refer to the new home,
//p4perl/main.

Presently Workshop-custom Swarm modules prevent a project
from follwoing paths anywhere outsuide //guest/<userid>/...;
once this restriction is lifted, the
//guest/perforce_software/p4perl/... path can be retired.
//p4perl/main/install_p4perl.sh
#3 21637 C. Thomas Tyler Enhanced install_p4perl.sh:

Added '-d' flag to provide a way to pass Perl configuration
parameters on to the Perl Configure utility, e.g. -Dusethreads.

Added '-f' flag to ignore failed Perl or P4Perl test suites.
Test suites are still run, but errors are converted to warnings
with this flag.

Added display of original command line to output, and minor
cosmetic tweaks.
#2 21619 C. Thomas Tyler Merged patch for @21616 from r16.1 to main:
Robustness enhancement to install_p4perl.sh.
#1 21609 C. Thomas Tyler Added install_p4perl.sh script to:
* Acquire and compile Perl source from CPAN on UNIX/Linux/OSX
  platforms.
* Acquire the Perforce C++ API.
* Acquire and compile P4Perl from The Workshop.

This script can be used to build Perl and P4Perl from scratch, or
to build P4Perl and append it to an existing Perl installation.