1 #!/bin/bash
   2 #
   3 # CDDL HEADER START
   4 #
   5 # The contents of this file are subject to the terms of the
   6 # Common Development and Distribution License, Version 1.0 only
   7 # (the "License").  You may not use this file except in compliance
   8 # with the License.
   9 #
  10 # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
  11 # or http://www.opensolaris.org/os/licensing.
  12 # See the License for the specific language governing permissions
  13 # and limitations under the License.
  14 #
  15 # When distributing Covered Code, include this CDDL HEADER in each
  16 # file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  17 # If applicable, add the following below this CDDL HEADER, with the
  18 # fields enclosed by brackets "[]" replaced with your own identifying
  19 # information: Portions Copyright [yyyy] [name of copyright owner]
  20 #
  21 # CDDL HEADER END
  22 #
  23 #
  24 # Copyright 2015 OmniTI Computer Consulting, Inc.  All rights reserved.
  25 # Use is subject to license terms.
  26 #
  27 
  28 umask 022
  29 
  30 #############################################################################
  31 # functions.sh
  32 #############################################################################
  33 # Helper functions for building packages that should be common to all build
  34 # scripts
  35 #############################################################################
  36 
  37 #############################################################################
  38 # Process command line options
  39 #############################################################################
  40 process_opts() {
  41     SCREENOUT=
  42     FLAVOR=
  43     OLDFLAVOR=
  44     BUILDARCH=both
  45     OLDBUILDARCH=
  46     BATCH=
  47     DEPVER=
  48     while getopts "bpf:ha:d:" opt; do
  49         case $opt in
  50             h)
  51                 show_usage
  52                 exit
  53                 ;;
  54             \?)
  55                 show_usage
  56                 exit 2
  57                 ;;
  58             p)
  59                 SCREENOUT=1
  60                 ;;
  61             b)
  62                 BATCH=1 # Batch mode - exit on error
  63                 ;;
  64             f)
  65                 FLAVOR=$OPTARG
  66                 OLDFLAVOR=$OPTARG # Used to see if the script overrides the
  67                                    # flavor
  68                 ;;
  69             a)
  70                 BUILDARCH=$OPTARG
  71                 OLDBUILDARCH=$OPTARG # Used to see if the script overrides the
  72                                      # BUILDARCH variable
  73                 if [[ "$BUILDARCH" != "32" && "$BUILDARCH" != "64" &&
  74                       "$BUILDARCH" != "both" ]]; then
  75                     echo "Invalid build architecture specified: $BUILDARCH"
  76                     show_usage
  77                     exit 2
  78                 fi
  79                 ;;
  80             d)
  81                 DEPVER=$OPTARG
  82                 ;;
  83         esac
  84     done
  85 }
  86 
  87 #############################################################################
  88 # Show usage information
  89 #############################################################################
  90 show_usage() {
  91     echo "Usage: $0 [-b] [-p] [-f FLAVOR] [-h] [-a 32|64|both] [-d DEPVER]"
  92     echo "  -b        : batch mode (exit on errors without asking)"
  93     echo "  -p        : output all commands to the screen as well as log file"
  94     echo "  -f FLAVOR : build a specific package flavor"
  95     echo "  -h        : print this help text"
  96     echo "  -a ARCH   : build 32/64 bit only, or both (default: both)"
  97     echo "  -d DEPVER : specify an extra dependency version (no default)"
  98 }
  99 
 100 #############################################################################
 101 # Log output of a command to a file
 102 #############################################################################
 103 logcmd() {
 104     if [[ -z "$SCREENOUT" ]]; then
 105         echo Running: "$@" >> $LOGFILE
 106         "$@" >> $LOGFILE 2>&1
 107     else
 108         echo Running: "$@" | tee $LOGFILE
 109         "$@" | tee $LOGFILE 2>&1
 110     fi
 111 }
 112 logmsg() {
 113     echo "$@" >> $LOGFILE
 114     echo "$@"
 115 }
 116 logerr() {
 117     # Print an error message and ask the user if they wish to continue
 118     logmsg $@
 119     if [[ -z $BATCH ]]; then
 120         ask_to_continue "An Error occured in the build. "
 121     else
 122         exit 1
 123     fi
 124 }
 125 ask_to_continue() {
 126     # Ask the user if they want to continue or quit
 127     echo -n "${1}Do you wish to continue anyway? (y/n) "
 128     read
 129     while [[ ! "$REPLY" =~ [yYnN] ]]; do
 130         echo -n "continue? (y/n) "
 131         read
 132     done
 133     if [[ "$REPLY" == "n" || "$REPLY" == "N" ]]; then
 134         logmsg "===== Build aborted ====="
 135         exit 1
 136     fi
 137     logmsg "===== User elected to continue after prompt. ====="
 138 }
 139 
 140 #############################################################################
 141 # URL encoding for package names, at least
 142 #############################################################################
 143 # This isn't real URL encoding, just a couple of common substitutions
 144 url_encode() {
 145     [ $# -lt 1 ] && logerr "Not enough arguments to url_encode().  Expecting a string to encode."
 146     local encoded="$1";
 147     encoded=`echo $encoded | sed -e 's!/!%2F!g' -e 's!+!%2B!g'`
 148     encoded=`echo $encoded | sed -e 's/%../_/g;'`
 149     echo $encoded
 150 }
 151 
 152 #############################################################################
 153 # Some initialization
 154 #############################################################################
 155 # Set the LANG to C as the assembler will freak out on unicode in headers
 156 LANG=C
 157 export LANG
 158 # Determine what release we're running as that affects some versions of things
 159 RELEASE=$(head -1 /etc/release | awk '{ print $3 }')
 160 # Set the path - This can be overriden/extended in the build script
 161 PATH="/usr/ccs/bin:/usr/bin:/usr/sbin:/usr/gnu/bin:/usr/sfw/bin"
 162 case ${RELEASE:1} in
 163     151004)
 164         PATH="/opt/gcc-4.6.3/bin:$PATH"
 165         GCC_CMD="/opt/gcc-4.6.3/bin/gcc"
 166         GCC_PKG="developer/gcc46"
 167         ;;
 168     151006)
 169         PATH="/opt/gcc-4.7.2/bin:$PATH"
 170         GCC_CMD="/opt/gcc-4.7.2/bin/gcc"
 171         GCC_PKG="developer/gcc47"
 172         ;;
 173     151008|151010|151012|151014)
 174         PATH="/opt/gcc-4.8.1/bin:$PATH"
 175         GCC_CMD="/opt/gcc-4.8.1/bin/gcc"
 176         GCC_PKG="developer/gcc48"
 177         ;;
 178     151016)
 179         PATH="/opt/gcc-5.1.0/bin:$PATH"
 180         GCC_CMD="/opt/gcc-5.1.0/bin/gcc"
 181         GCC_PKG="developer/gcc51"
 182         ;;
 183     *)
 184         logerr "Release $RELEASE not supported for omniti-ms"
 185         ;;
 186 esac
 187 export PATH
 188 # The dir where this file is located - used for sourcing further files
 189 MYDIR=$PWD/`dirname $BASH_SOURCE[0]`
 190 # The dir where this file was sourced from - this will be the directory of the
 191 # build script
 192 SRCDIR=$PWD/`dirname $0`
 193 
 194 #############################################################################
 195 # Load configuration options
 196 #############################################################################
 197 . $MYDIR/config.sh
 198 . $MYDIR/site.sh
 199 
 200 # Platform information
 201 SUNOSVER=`uname -r` # e.g. 5.11
 202 
 203 if [[ -f $LOGFILE ]]; then
 204     mv $LOGFILE $LOGFILE.1
 205 fi
 206 process_opts $@
 207 
 208 BasicRequirements(){
 209     local needed=""
 210     [[ -x $GCC_CMD ]] || needed+=" $GCC_PKG"
 211     [[ -x /usr/bin/ar ]] || needed+=" developer/object-file"
 212     [[ -x /usr/bin/ld ]] || needed+=" developer/linker"
 213     [[ -f /usr/lib/crt1.o ]] || needed+=" developer/library/lint"
 214     [[ -x /usr/bin/gmake ]] || needed+=" developer/build/gnu-make"
 215     [[ -f /usr/include/sys/types.h ]] || needed+=" system/header"
 216     [[ -f /usr/include/math.h ]] || needed+=" system/library/math/header-math"
 217     [[ -x /usr/bin/rsync ]] || needed+=" network/rsync"
 218     if [[ -n "$needed" ]]; then
 219         logmsg "You appear to be missing some basic build requirements."
 220         logmsg "To fix this run:"
 221         logmsg " "
 222         logmsg "  sudo pkg install$needed"
 223         logerr
 224     fi
 225 }
 226 BasicRequirements
 227 
 228 #############################################################################
 229 # Running as root is not safe
 230 #############################################################################
 231 if [[ "$UID" = "0" ]]; then
 232     logerr "--- You cannot run this as root"
 233 fi
 234 
 235 #############################################################################
 236 # Print startup message
 237 #############################################################################
 238 [[ -z "$NOBANNER" ]] && logmsg "===== Build started at `date` ====="
 239 #############################################################################
 240 # Initialization function
 241 #############################################################################
 242 init() {
 243     # Print out current settings
 244     logmsg "Package name: $PKG"
 245 
 246     # In the ms.omniti.com repo, we want all pkg FMRIs to start with "omniti/"
 247     FMRI_PREFIX=${PKG:0:7}
 248     if [[ ! $FMRI_PREFIX = "omniti/" ]]; then
 249         logerr "Package name should begin with \"omniti/\". Please change the value of PKG in the build script."
 250     fi
 251 
 252     # Selected flavor
 253     if [[ -z "$FLAVOR" ]]; then
 254         logmsg "Selected flavor: None (use -f to specify a flavor)"
 255     else
 256         logmsg "Selected Flavor: $FLAVOR"
 257     fi
 258     if [[ -n "$OLDFLAVOR" && "$OLDFLAVOR" != "$FLAVOR" ]]; then
 259         logmsg "NOTICE - The flavor was overridden by the build script."
 260         logmsg "The flavor specified on the command line was: $OLDFLAVOR"
 261     fi
 262     # Build arch
 263     logmsg "Selected build arch: $BUILDARCH"
 264     if [[ -n "$OLDBUILDARCH" && "$OLDBUILDARCH" != "$BUILDARCH" ]]; then
 265         logmsg "NOTICE - The build arch was overridden by the build script."
 266         logmsg "The build arch specified on the command line was: $OLDFLAVOR"
 267     fi
 268     # Extra dependency version
 269     if [[ -z "$DEPVER" ]]; then
 270         logmsg "Extra dependency: None (use -d to specify a version)"
 271     else
 272         logmsg "Extra dependency: $DEPVER"
 273     fi
 274     # Ensure SUMMARY and DESC are non-empty
 275     if [[ -z "$SUMMARY" ]]; then
 276         logerr "SUMMARY may not be empty. Please update your build script"
 277     elif [[ -z "$DESC" ]]; then
 278         logerr "DESC may not be empty. Please update your build script"
 279     fi
 280 
 281     # BUILDDIR can be used to manually specify what directory the program is
 282     # built in (i.e. what the tarball extracts to). This defaults to the name
 283     # and version of the program, which works in most cases.
 284     if [[ -z $BUILDDIR ]]; then
 285         BUILDDIR=$PROG-$VER
 286     fi
 287 
 288     RPATH=`echo $PKGSRVR | sed -e 's/^file:\/*/\//'`
 289     if [[ "$RPATH" != "$PKGSRVR" ]]; then
 290         if [[ ! -d $RPATH ]]; then
 291             pkgrepo create $RPATH || \
 292                 logerr "Could not local repo"
 293             pkgrepo add-publisher -s $RPATH $PKGPUBLISHER || \
 294                 logerr "Could not set publisher on repo"
 295         fi
 296     fi
 297     pkgrepo get -s $PKGSRVR > /dev/null 2>&1 || \
 298         logerr "The PKGSRVR ($PKGSRVR) isn't available. All is doomed."
 299     verify_depends
 300 }
 301 
 302 #############################################################################
 303 # Verify any dependencies
 304 #############################################################################
 305 verify_depends() {
 306     logmsg "Verifying build dependencies"
 307     [[ -z "$BUILD_DEPENDS_IPS" ]] && BUILD_DEPENDS_IPS=$BUILD_DEPENDS
 308     for i in $BUILD_DEPENDS_IPS; do
 309         # Trim indicators to get the true name (see make_package for details)
 310         case ${i:0:1} in
 311             \=|\?)
 312                 i=${i:1}
 313                 ;;
 314             \-)
 315                 # If it's an exclude, we should error if it's installed rather than missing
 316                 i=${i:1}
 317                 pkg info $i > /dev/null 2<&1 &&
 318                     logerr "--- Excluded dependency $i cannot be installed with this package."
 319                 continue
 320                 ;;
 321         esac
 322         pkg info $i > /dev/null 2<&1 ||
 323             logerr "--- Build dependency $i not found"
 324     done
 325 }
 326 
 327 #############################################################################
 328 # People that need this should call it explicitly
 329 #############################################################################
 330 run_autoconf() {
 331     logmsg "Running autoconf"
 332     pushd $TMPDIR/$BUILDDIR > /dev/null
 333     logcmd autoconf || logerr "Failed to run autoconf"
 334     popd > /dev/null
 335 }
 336 
 337 run_autogen() {
 338     logmsg "Running autogen.sh"
 339     pushd $TMPDIR/$BUILDDIR > /dev/null
 340     CFLAGS="$CFLAGS32 $CFLAGS" \
 341     CXXFLAGS="$CXXFLAGS32 $CXXFLAGS" \
 342     CPPFLAGS="$CPPFLAGS32 $CPPFLAGS" \
 343     LDFLAGS="$LDFLAGS32 $LDFLAGS" \
 344     CC=$CC CXX=$CXX \
 345     logcmd ./autogen.sh $CONFIGURE_OPTS_32 $CONFIGURE_OPTS_64 $CONFIGURE_OPTS || \
 346         logerr "Failed to run autogen.sh"
 347     popd > /dev/null
 348 }
 349 
 350 #############################################################################
 351 # Stuff that needs to be done/set before we start building
 352 #############################################################################
 353 prep_build() {
 354     logmsg "Preparing for build"
 355 
 356     # Get the current date/time for the package timestamp
 357     DATETIME=`TZ=UTC /usr/bin/date +"%Y%m%dT%H%M%SZ"`
 358 
 359     logmsg "--- Creating temporary install dir"
 360     # We might need to encode some special chars
 361     PKGE=$(url_encode $PKG)
 362     # For DESTDIR the '%' can cause problems for some install scripts
 363     PKGD=${PKGE//%/_}
 364     DESTDIR=$DTMPDIR/${PKGD}_pkg
 365     if [[ -z $DONT_REMOVE_INSTALL_DIR ]]; then
 366         logcmd chmod -R u+w $DESTDIR > /dev/null 2>&1
 367         logcmd rm -rf $DESTDIR || \
 368             logerr "Failed to remove old temporary install dir"
 369         mkdir -p $DESTDIR || \
 370             logerr "Failed to create temporary install dir"
 371     fi
 372 }
 373 
 374 #############################################################################
 375 # Applies patches contained in $PATCHDIR (default patches/)
 376 #############################################################################
 377 check_for_patches() {
 378     if [[ -z $1 ]]; then
 379         logmsg "Checking for patches in $PATCHDIR/"
 380     else
 381         logmsg "Checking for patches in $PATCHDIR/ ($1)"
 382     fi
 383     if [[ ! -d $SRCDIR/$PATCHDIR ]]; then
 384         logmsg "--- No patches directory found"
 385         return 1
 386     fi
 387     if [[ ! -f $SRCDIR/$PATCHDIR/series ]]; then
 388         logmsg "--- No series file (list of patches) found"
 389         return 1
 390     fi
 391     return 0
 392 }
 393 
 394 patch_source() {
 395     if ! check_for_patches "in order to apply them"; then
 396         logmsg "--- Not applying any patches"
 397     else
 398         logmsg "Applying patches"
 399         # Read the series file for patch filenames
 400         exec 3<"$SRCDIR/$PATCHDIR/series" # Open the series file with handle 3
 401         pushd $TMPDIR/$BUILDDIR > /dev/null
 402         while read LINE <&3 ; do
 403             # Split Line into filename+args
 404             patch_file $LINE
 405         done
 406         popd > /dev/null
 407         exec 3<&- # Close the file
 408     fi
 409 }
 410 
 411 patch_file() {
 412     FILENAME=$1
 413     shift
 414     ARGS=$@
 415     if [[ ! -f $SRCDIR/$PATCHDIR/$FILENAME ]]; then
 416         logmsg "--- Patch file $FILENAME not found. Skipping patch."
 417         return
 418     fi
 419     # Note - if -p is specified more than once, then the last one takes
 420     # precedence, so we can specify -p1 at the beginning to default to -p1.
 421     # -t - don't ask questions
 422     # -N - don't try to apply a reverse patch
 423     if ! logcmd $PATCH -p1 -t -N $ARGS < $SRCDIR/$PATCHDIR/$FILENAME; then
 424         logerr "--- Patch $FILENAME failed"
 425     else
 426         logmsg "--- Applied patch $FILENAME"
 427     fi
 428 }
 429 
 430 #############################################################################
 431 # Download source from git
 432 #############################################################################
 433 # Parameters
 434 #   $1 - repos
 435 #   $2 - branch
 436 #   $3 - commit
 437 #   $4 - version
 438 #
 439 # E.g.
 440 #       download_git https://github.com/omniti-labs/nab master HEAD
 441 download_git() {
 442     local REPOS=$1
 443     local BRANCH=$2
 444     local COMMIT=$3
 445     local VERSION=$4
 446     if [ -n "$BRANCH" ]; then
 447         BRANCH="master"
 448     fi
 449     if [ -n "$COMMIT" ]; then
 450         COMMIT="HEAD"
 451     fi
 452     pushd $TMPDIR > /dev/null
 453     logmsg "Checking for source directory"
 454     if [ -d $BUILDDIR ]; then
 455         logmsg "--- removing previous source checkout"
 456         logcmd rm -rf $BUILDDIR
 457     fi
 458     logmsg "Checking code out from git repo"
 459     logcmd $GIT clone $REPOS $BUILDDIR
 460     pushd $BUILDDIR > /dev/null
 461     if [ -n "$COMMIT" ]; then
 462         logcmd $GIT checkout $COMMIT
 463     fi
 464     if [ -n "$VERSION" ]; then
 465         VER=$VERSION
 466         VERHUMAN=$VER
 467     else
 468         REV=`$GIT log -1  --format=format:%at`
 469         REVDATE=`echo $REV | gawk '{ print strftime("%c %Z",$1) }'`
 470         VER=0.1.$REV
 471         VERHUMAN="checkout from $REVDATE"
 472     fi
 473     popd > /dev/null
 474     popd > /dev/null
 475 }
 476 
 477 #############################################################################
 478 # Download source tarball if needed and extract it
 479 #############################################################################
 480 # Parameters
 481 #   $1 - directory name on the server
 482 #   $2 - program name
 483 #   $3 - program version
 484 #   $4 - target directory
 485 #
 486 # E.g.
 487 #       download_source myprog myprog 1.2.3 will try:
 488 #       http://mirrors.omniti.com/myprog/myprog-1.2.3.tar.gz
 489 download_source() {
 490     local DLDIR=$1
 491     local PROG=$2
 492     local VER=$3
 493     local TARGETDIR=$4
 494     if [[ -z $VER ]]; then
 495         local ARCHIVEPREFIX=$PROG
 496     else
 497         local ARCHIVEPREFIX=$PROG-$VER
 498     fi
 499     if [[ -z $TARGETDIR ]]; then
 500         # Default to $TMPDIR if no output dir specified
 501         TARGETDIR=$TMPDIR
 502     fi
 503     # Create TARGETDIR if it doesn't exist
 504     if [[ ! -d $TARGETDIR ]]; then
 505         logmsg "Specified target directory $TARGETDIR does not exist.  Creating it now."
 506         logcmd mkdir -p $TARGETDIR
 507     fi
 508     pushd $TARGETDIR > /dev/null
 509     logmsg "Checking for source directory"
 510     if [ -d $BUILDDIR ]; then
 511         logmsg "--- Source directory found"
 512         if check_for_patches "to see if we need to remove the source dir"; then
 513             logmsg "--- Patches are present, removing source directory"
 514             logcmd rm -rf $BUILDDIR || \
 515                 logerr "Failed to remove source directory"
 516         else
 517             logmsg "--- Patches are not present, keeping source directory"
 518             popd > /dev/null
 519             return
 520         fi
 521     else
 522         logmsg "--- Source directory not found"
 523     fi
 524 
 525     # If we reach this point, the source directory was either not found, or it
 526     # was removed due to patches being present.
 527     logmsg "Checking for $PROG source archive"
 528     find_archive $ARCHIVEPREFIX FILENAME
 529     if [[ "$FILENAME" == "" ]]; then
 530         # Try all possible archive names
 531         logmsg "--- Archive not found."
 532         logmsg "Downloading archive"
 533         URLPREFIX=http://$MIRROR/$DLDIR/$ARCHIVEPREFIX
 534         $WGET -a $LOGFILE $URLPREFIX.tar.gz || \
 535             $WGET -a $LOGFILE $URLPREFIX.tar.bz2 || \
 536             $WGET -a $LOGFILE $URLPREFIX.tar.xz || \
 537             $WGET -a $LOGFILE $URLPREFIX.tgz || \
 538             $WGET -a $LOGFILE $URLPREFIX.tbz || \
 539             $WGET -a $LOGFILE $URLPREFIX.tar || \
 540             logerr "--- Failed to download file"
 541         find_archive $ARCHIVEPREFIX FILENAME
 542         if [[ "$FILENAME" == "" ]]; then
 543             logerr "Unable to find downloaded file."
 544         fi
 545     else
 546         logmsg "--- $PROG source archive found"
 547     fi
 548     # Extract the archive
 549     logmsg "Extracting archive: $FILENAME"
 550     if ! logcmd extract_archive $FILENAME; then
 551         logerr "--- Unable to extract archive."
 552     fi
 553     # Make sure the archive actually extracted some source where we expect
 554     if [[ ! -d $BUILDDIR ]]; then
 555         logerr "--- Extracted source is not in the expected location" \
 556             " ($BUILDDIR)"
 557     fi
 558     popd > /dev/null
 559 }
 560 
 561 # Finds an existing archive and stores its value in a variable whose name
 562 #   is passed as a second parameter
 563 # Example: find_archive myprog-1.2.3 FILENAME
 564 #   Stores myprog-1.2.3.tar.gz in $FILENAME
 565 find_archive() {
 566     FILES=`ls $1.{tar.bz2,tar.gz,tar.xz,tgz,tbz,tar} 2>/dev/null`
 567     FILES=${FILES%% *} # Take only the first filename returned
 568     # This dereferences the second parameter passed
 569     eval "$2=\"$FILES\""
 570 }
 571 
 572 # Extracts an archive regardless of its extension
 573 extract_archive() {
 574     if [[ ${1: -7} == ".tar.gz" || ${1: -4} == ".tgz" ]]; then
 575         $GZIP -dc $1 | $TAR xvf -
 576     elif [[ ${1: -8} == ".tar.bz2" || ${1: -4} == ".tbz" ]]; then
 577         $BUNZIP2 -dc $1 | $TAR xvf -
 578     elif [[ ${1: -7} == ".tar.xz" ]]; then
 579         $XZCAT $1 | $TAR xvf -
 580     elif [[ ${1: -4} == ".tar" ]]; then
 581         $TAR xvf $1
 582     else
 583         return 1
 584     fi
 585 }
 586 
 587 #############################################################################
 588 # Make the package
 589 #############################################################################
 590 make_package() {
 591     logmsg "Making package"
 592     PKGSEND=/usr/bin/pkgsend
 593     PKGDEPEND=/usr/bin/pkgdepend
 594     PKGMOGRIFY=/usr/bin/pkgmogrify
 595     PKGFMT=/usr/bin/pkgfmt
 596     P5M_INT=$TMPDIR/${PKGE}.p5m.int
 597     P5M_FINAL=$TMPDIR/${PKGE}.p5m
 598     GLOBAL_MOG_FILE=$MYDIR/global-transforms.mog
 599     MY_MOG_FILE=$TMPDIR/${PKGE}.mog
 600 
 601     ## Strip leading zeros in version components.
 602     VER=`echo $VER | sed -e 's/\.0*\([1-9]\)/.\1/g;'`
 603     FMRI="${PKG}@${VER},${SUNOSVER}-${PVER}"
 604     if [[ -n "$DESTDIR" ]]; then
 605         logmsg "--- Generating package manifest from $DESTDIR"
 606         logmsg "------ Running: $PKGSEND generate $DESTDIR > $P5M_INT"
 607         $PKGSEND generate $DESTDIR > $P5M_INT || \
 608             logerr "------ Failed to generate manifest"
 609     else
 610         logmsg "--- Looks like a meta-package. Creating empty manifest"
 611         logcmd touch $P5M_INT || \
 612             logerr "------ Failed to create empty manifest"
 613     fi
 614     logmsg "--- Generating package metadata"
 615     echo "set name=pkg.fmri value=$FMRI" > $MY_MOG_FILE
 616     # Set human-readable version, if it exists
 617     if [[ -n "$VERHUMAN" ]]; then
 618         logmsg "------ Setting human-readable version"
 619         echo "set name=pkg.human-version value=\"$VERHUMAN\"" >> $MY_MOG_FILE
 620     fi
 621     echo "set name=pkg.summary value=\"$SUMMARY\"" >> $MY_MOG_FILE
 622     echo "set name=pkg.descr value=\"$DESC\"" >> $MY_MOG_FILE
 623     echo "set name=publisher value=\"sa@omniti.com\"" >> $MY_MOG_FILE
 624     if [[ -n "$DEPENDS_IPS" ]]; then
 625         logmsg "------ Adding dependencies"
 626         for i in $DEPENDS_IPS; do
 627             # IPS dependencies have multiple types, of which we care about four:
 628             #    require, optional, incorporate, exclude
 629             # For backward compatibility, assume no indicator means type=require
 630             # FMRI attributes are implicitly rooted so we don't have to prefix
 631             # 'pkg:/' or worry about ambiguities in names
 632             local DEPTYPE="require"
 633             case ${i:0:1} in
 634                 \=)
 635                     DEPTYPE="incorporate"
 636                     i=${i:1}
 637                     ;;
 638                 \?)
 639                     DEPTYPE="optional"
 640                     i=${i:1}
 641                     ;;
 642                 \-)
 643                     DEPTYPE="exclude"
 644                     i=${i:1}
 645                     ;;
 646             esac
 647             echo "depend type=$DEPTYPE fmri=${i}" >> $MY_MOG_FILE
 648         done
 649     fi
 650     if [[ -f $SRCDIR/local.mog ]]; then
 651         LOCAL_MOG_FILE=$SRCDIR/local.mog
 652     fi
 653     logmsg "--- Applying transforms"
 654     $PKGMOGRIFY $P5M_INT $MY_MOG_FILE $GLOBAL_MOG_FILE $LOCAL_MOG_FILE $* > $P5M_INT.stage1
 655     if [[ -z "$NO_AUTO_DEPENDS" ]]; then
 656         $PKGDEPEND generate -d $DESTDIR $P5M_INT.stage1 > $P5M_INT.dep
 657         $PKGDEPEND resolve $P5M_INT.dep
 658         cat $P5M_INT.dep.res >> $P5M_INT.stage1
 659         # Use an "optional" dependency on the 'entire' metapackage so that
 660         # we don't let a later version for an unsupported early OmniOS install.
 661         # For example:
 662         #     foo@1.9-0.151006   exists.
 663         # We update it to:
 664         #     foo@1.10-0.151014
 665         # and in both cases make it *lock* to entire@11-0.151XXX as appropriate.
 666         # So foo@1.10 won't install on any OmniOS older than r151014.
 667         # This used to be *incorporate* but that put both a floor AND a ceiling
 668         # on the revision of OmniOS, and having just a floor is sufficient
 669         # thanks to illumos's versioned libraries and backward compatibility.
 670         # If a user uninstalls "entire", they go beyond the realm of support.
 671         # Some users can do this safely (developers who use onu(1ONBLD), e.g.).
 672         echo "depend fmri=pkg:/entire@11-$PVER type=optional" >> $P5M_INT.stage1
 673     fi
 674     $PKGFMT -u < $P5M_INT.stage1 > $P5M_FINAL
 675     logmsg "--- Publishing package to $PKGSRVR"
 676     if [[ -z "$BATCH" ]]; then
 677         ask_to_continue "Last chance to sanity-check before publication! "
 678     fi
 679     if [[ -n "$DESTDIR" ]]; then
 680         logcmd $PKGSEND -s $PKGSRVR publish -d $DESTDIR -d $TMPDIR/$BUILDDIR \
 681             -d $SRCDIR -T \*.py $P5M_FINAL || \
 682             logerr "------ Failed to publish package"
 683     else
 684         # If we're a metapackage (no DESTDIR) then there are no directories to check
 685         logcmd $PKGSEND -s $PKGSRVR publish $P5M_FINAL || \
 686             logerr "------ Failed to publish package"
 687     fi
 688     logmsg "--- Published $FMRI" 
 689 }
 690 
 691 #############################################################################
 692 # Make isaexec stub binaries
 693 #############################################################################
 694 make_isa_stub() {
 695     logmsg "Making isaexec stub binaries"
 696     [[ -z $ISAEXEC_DIRS ]] && ISAEXEC_DIRS="bin sbin"
 697     for DIR in $ISAEXEC_DIRS; do
 698         if [[ -d $DESTDIR$PREFIX/$DIR ]]; then
 699             logmsg "--- $DIR"
 700             pushd $DESTDIR$PREFIX/$DIR > /dev/null
 701             make_isaexec_stub_arch $ISAPART
 702             make_isaexec_stub_arch $ISAPART64
 703             popd > /dev/null
 704         fi
 705     done
 706 }
 707 
 708 make_isaexec_stub_arch() {
 709     for file in $1/*; do
 710         [[ -f $file ]] || continue # Deals with empty dirs & non-files
 711         # Check to make sure we don't have a script
 712         read -n 5 < $file
 713         file=`echo $file | sed -e "s/$1\///;"`
 714         # Skip if we already made a stub for this file
 715         [[ -f $file ]] && continue
 716         # Only copy non-binaries if we set NOSCRIPTSTUB
 717         if [[ $REPLY != $'\177'ELF && -n "$NOSCRIPTSTUB" ]]; then
 718             logmsg "------ Non-binary file: $file - copying instead"
 719             cp $1/$file .
 720             chmod +x $file
 721             continue
 722         fi
 723         logmsg "------ $file"
 724         # Run the makeisa.sh script
 725         CC=$CC \
 726         logcmd $MYDIR/makeisa.sh $PREFIX/$DIR $file || \
 727             logerr "--- Failed to make isaexec stub for $DIR/$file"
 728     done
 729 }
 730 
 731 #############################################################################
 732 # Build commands
 733 #############################################################################
 734 # Notes:
 735 #   - These methods are designed to work in the general case.
 736 #   - You can set CFLAGS/LDFLAGS (and CFLAGS32/CFLAGS64 for arch specific flags)
 737 #   - Configure flags are set in CONFIGURE_OPTS_32 and CONFIGURE_OPTS_64 with
 738 #     defaults set in config.sh. You can append to these variables or replace
 739 #     them if the defaults don't work for you.
 740 #   - In the normal case, where you just want to add --enable-feature, set
 741 #     CONFIGURE_OPTS. This will be appended to the end of CONFIGURE_CMD
 742 #     for both 32 and 64 bit builds.
 743 #   - Any of these functions can be overriden in your build script, so if
 744 #     anything here doesn't apply to the build process for your application,
 745 #     just override that function with whatever code you need. The build
 746 #     function itself can be overriden if the build process doesn't fit into a
 747 #     configure, make, make install pattern.
 748 #############################################################################
 749 make_clean() {
 750     logmsg "--- make (dist)clean"
 751     logcmd $MAKE distclean || \
 752     logcmd $MAKE clean || \
 753         logmsg "--- *** WARNING *** make (dist)clean Failed"
 754 }
 755 
 756 configure32() {
 757     logmsg "--- configure (32-bit)"
 758     CFLAGS="$CFLAGS32 $CFLAGS" \
 759     CXXFLAGS="$CXXFLAGS32 $CXXFLAGS" \
 760     CPPFLAGS="$CPPFLAGS32 $CPPFLAGS" \
 761     LDFLAGS="$LDFLAGS32 $LDFLAGS" \
 762     CC=$CC CXX=$CXX \
 763     logcmd $CONFIGURE_CMD $CONFIGURE_OPTS_32 \
 764     $CONFIGURE_OPTS || \
 765         logerr "--- Configure failed"
 766 }
 767 
 768 configure64() {
 769     logmsg "--- configure (64-bit)"
 770     CFLAGS="$CFLAGS64 $CFLAGS" \
 771     CXXFLAGS="$CXXFLAGS64 $CXXFLAGS" \
 772     CPPFLAGS="$CPPFLAGS64 $CPPFLAGS" \
 773     LDFLAGS="$LDFLAGS64 $LDFLAGS" \
 774     CC=$CC CXX=$CXX \
 775     logcmd $CONFIGURE_CMD $CONFIGURE_OPTS_64 \
 776     $CONFIGURE_OPTS || \
 777         logerr "--- Configure failed"
 778 }
 779 
 780 make_prog() {
 781     [[ -n $NO_PARALLEL_MAKE ]] && MAKE_JOBS=""
 782     logmsg "--- make"
 783     logcmd $MAKE $MAKE_JOBS || \
 784         logerr "--- Make failed"
 785 }
 786 
 787 make_prog32() {
 788     make_prog
 789 }
 790 
 791 make_prog64() {
 792     make_prog
 793 }
 794 
 795 make_install() {
 796     logmsg "--- make install"
 797     logcmd $MAKE DESTDIR=${DESTDIR} install || \
 798         logerr "--- Make install failed"
 799 }
 800 
 801 make_install32() {
 802     make_install
 803 }
 804 
 805 make_install64() {
 806     make_install
 807 }
 808 
 809 make_pure_install() {
 810     # Make pure_install for perl modules so they don't touch perllocal.pod
 811     logmsg "--- make install (pure)"
 812     logcmd $MAKE DESTDIR=${DESTDIR} pure_install || \
 813         logerr "--- Make pure_install failed"
 814 }
 815 
 816 make_param() {
 817     logmsg "--- make $@"
 818     logcmd $MAKE "$@" || \
 819         logerr "--- $MAKE $1 failed"
 820 }
 821 
 822 # Helper function that can be called by build scripts to make in a specific dir
 823 make_in() {
 824     [[ -z $1 ]] && logerr "------ Make in dir failed - no dir specified"
 825     [[ -n $NO_PARALLEL_MAKE ]] && MAKE_JOBS=""
 826     logmsg "------ make in $1"
 827     logcmd $MAKE $MAKE_JOBS -C $1 || \
 828         logerr "------ Make in $1 failed"
 829 }
 830 
 831 # Helper function that can be called by build scripts to install in a specific
 832 # dir
 833 make_install_in() {
 834     [[ -z $1 ]] && logerr "--- Make install in dir failed - no dir specified"
 835     logmsg "------ make install in $1"
 836     logcmd $MAKE -C $1 DESTDIR=${DESTDIR} install || \
 837         logerr "------ Make install in $1 failed"
 838 }
 839 
 840 build() {
 841     if [[ $BUILDARCH == "32" || $BUILDARCH == "both" ]]; then
 842         build32
 843     fi
 844     if [[ $BUILDARCH == "64" || $BUILDARCH == "both" ]]; then
 845         build64
 846     fi
 847 }
 848 
 849 build32() {
 850     pushd $TMPDIR/$BUILDDIR > /dev/null
 851     logmsg "Building 32-bit"
 852     export ISALIST="$ISAPART"
 853     make_clean
 854     configure32
 855     make_prog32
 856     make_install32
 857     popd > /dev/null
 858     unset ISALIST
 859     export ISALIST
 860 }
 861 
 862 build64() {
 863     pushd $TMPDIR/$BUILDDIR > /dev/null
 864     logmsg "Building 64-bit"
 865     make_clean
 866     configure64
 867     make_prog64
 868     make_install64
 869     popd > /dev/null
 870 }
 871 
 872 #############################################################################
 873 # Build function for python programs
 874 #############################################################################
 875 pre_python_32() {
 876     logmsg "prepping 32bit python build"
 877 }
 878 pre_python_64() {
 879     logmsg "prepping 64bit python build"
 880 }
 881 python_build() {
 882     if [[ -z "$PYTHON" ]]; then logerr "PYTHON not set"; fi
 883     if [[ -z "$PYTHONPATH" ]]; then logerr "PYTHONPATH not set"; fi
 884     if [[ -z "$PYTHONLIB" ]]; then logerr "PYTHONLIB not set"; fi
 885     logmsg "Building using python setup.py"
 886     pushd $TMPDIR/$BUILDDIR > /dev/null
 887 
 888     if [[ $BUILDARCH == "32" || $BUILDARCH == "both" ]]; then
 889         buildpython32
 890     fi
 891     if [[ $BUILDARCH == "64" || $BUILDARCH == "both" ]]; then
 892         buildpython64
 893     fi
 894     popd > /dev/null
 895 }
 896 
 897 buildpython32() {
 898     ISALIST=i386
 899     export ISALIST
 900     pre_python_32
 901     logmsg "--- setup.py (32) build"
 902     CFLAGS="$CFLAGS32 $CFLAGS" \
 903     CXXFLAGS="$CXXFLAGS32 $CXXFLAGS" \
 904     CPPFLAGS="$CPPFLAGS32 $CPPFLAGS" \
 905     LDFLAGS="$LDFLAGS32 $LDFLAGS" \
 906     CC=$CC CXX=$CXX \
 907     logcmd $PYTHON ./setup.py build ||
 908         logerr "--- build failed"
 909     logmsg "--- setup.py (32) install"
 910     logcmd $PYTHON \
 911         ./setup.py install --root=$DESTDIR ||
 912         logerr "--- install failed"
 913 }
 914 
 915 buildpython64(){
 916     ISALIST="amd64 i386"
 917     export ISALIST
 918     pre_python_64
 919     logmsg "--- setup.py (64) build"
 920     CFLAGS="$CFLAGS64 $CFLAGS" \
 921     CXXFLAGS="$CXXFLAGS64 $CXXFLAGS" \
 922     CPPFLAGS="$CPPFLAGS64 $CPPFLAGS" \
 923     LDFLAGS="$LDFLAGS64 $LDFLAGS" \
 924     CC=$CC CXX=$CXX \
 925     logcmd $PYTHON ./setup.py build ||
 926         logerr "--- build failed"
 927     logmsg "--- setup.py (64) install"
 928     logcmd $PYTHON \
 929         ./setup.py install --root=$DESTDIR ||
 930         logerr "--- install failed"
 931 }
 932 
 933 #############################################################################
 934 # Build/test function for perl modules
 935 #############################################################################
 936 # Detects whether to use Build.PL or Makefile.PL
 937 # Note: Build.PL probably needs Module::Build installed
 938 #############################################################################
 939 vendorizeperl() {
 940     logcmd mv $DESTDIR/usr/perl5/lib/site_perl $DESTDIR/usr/perl5/vendor_perl || logerr "can't move to vendor_perl"
 941     logcmd mkdir -p $DESTDIR/usr/perl5/${DEPVER}
 942     logcmd mv $DESTDIR/usr/perl5/man $DESTDIR/usr/perl5/${DEPVER}/man || logerr "can't move perl man"
 943 }
 944 
 945 buildperl() {
 946     if [[ -f $SRCDIR/${PROG}-${VER}.env ]]; then
 947         logmsg "Sourcing environment file: $SRCDIR/${PROG}-${VER}.env"
 948         source $SRCDIR/${PROG}-${VER}.env
 949     fi
 950     if [[ $BUILDARCH == "32" || $BUILDARCH == "both" ]]; then
 951         buildperl32
 952     fi
 953     if [[ $BUILDARCH == "64" || $BUILDARCH == "both" ]]; then
 954         buildperl64
 955     fi
 956 }
 957 
 958 buildperl32() {
 959     if [[ -f $SRCDIR/${PROG}-${VER}.env32 ]]; then
 960         logmsg "Sourcing environment file: $SRCDIR/${PROG}-${VER}.env32"
 961         source $SRCDIR/${PROG}-${VER}.env32
 962     fi
 963     pushd $TMPDIR/$BUILDDIR > /dev/null
 964     logmsg "Building 32-bit"
 965     export ISALIST="$ISAPART"
 966     local OPTS
 967     OPTS=${MAKEFILE_OPTS//_ARCH_/}
 968     OPTS=${OPTS//_ARCHBIN_/$ISAPART}
 969     if [[ -f Makefile.PL ]]; then
 970         make_clean
 971         makefilepl32 $OPTS
 972         make_prog
 973         [[ -n $PERL_MAKE_TEST ]] && make_param test
 974         make_pure_install
 975     elif [[ -f Build.PL ]]; then
 976         build_clean
 977         buildpl32 $OPTS
 978         build_prog
 979         [[ -n $PERL_MAKE_TEST ]] && build_test
 980         build_install
 981     fi
 982     popd > /dev/null
 983     unset ISALIST
 984     export ISALIST
 985 }
 986 
 987 buildperl64() {
 988     if [[ -f $SRCDIR/${PROG}-${VER}.env64 ]]; then
 989         logmsg "Sourcing environment file: $SRCDIR/${PROG}-${VER}.env64"
 990         source $SRCDIR/${PROG}-${VER}.env64
 991     fi
 992     pushd $TMPDIR/$BUILDDIR > /dev/null
 993     logmsg "Building 64-bit"
 994     local OPTS
 995     OPTS=${MAKEFILE_OPTS//_ARCH_/$ISAPART64}
 996     OPTS=${OPTS//_ARCHBIN_/$ISAPART64}
 997     if [[ -f Makefile.PL ]]; then
 998         make_clean
 999         makefilepl64 $OPTS
1000         make_prog
1001         [[ -n $PERL_MAKE_TEST ]] && make_param test
1002         make_pure_install
1003     elif [[ -f Build.PL ]]; then
1004         build_clean
1005         buildpl64 $OPTS
1006         build_prog
1007         [[ -n $PERL_MAKE_TEST ]] && build_test
1008         build_install
1009     fi
1010     popd > /dev/null
1011 }
1012 
1013 makefilepl32() {
1014     logmsg "--- Makefile.PL 32-bit"
1015     logcmd $PERL32 Makefile.PL PREFIX=$PREFIX $@ ||
1016         logerr "Failed to run Makefile.PL"
1017 }
1018 
1019 makefilepl64() {
1020     logmsg "--- Makefile.PL 64-bit"
1021     logcmd $PERL64 Makefile.PL PREFIX=$PREFIX $@ ||
1022         logerr "Failed to run Makefile.PL"
1023 }
1024 
1025 buildpl32() {
1026     logmsg "--- Build.PL 32-bit"
1027     logcmd $PERL32 Build.PL prefix=$PREFIX $@ ||
1028         logerr "Failed to run Build.PL"
1029 }
1030 
1031 buildpl64() {
1032     logmsg "--- Build.PL 64-bit"
1033     logcmd $PERL64 Build.PL prefix=$PREFIX $@ ||
1034         logerr "Failed to run Build.PL"
1035 }
1036 
1037 build_clean() {
1038     logmsg "--- Build (dist)clean"
1039     logcmd ./Build distclean || \
1040     logcmd ./Build clean || \
1041         logmsg "--- *** WARNING *** make (dist)clean Failed"
1042 }
1043 
1044 build_prog() {
1045     logmsg "--- Build"
1046     logcmd ./Build ||
1047         logerr "Build failed"
1048 }
1049 
1050 build_test() {
1051     logmsg "--- Build test"
1052     logcmd ./Build test ||
1053         logerr "Build test failed"
1054 }
1055 
1056 build_install() {
1057     logmsg "--- Build install"
1058     logcmd ./Build pure_install --destdir=$DESTDIR || \
1059         logmsg "Build install failed"
1060 }
1061 
1062 test_if_core() {
1063     logmsg "Testing whether $MODNAME is in core"
1064     logmsg "--- Ensuring ${PKG} is not installed"
1065     if logcmd pkg info ${PKG}; then
1066         logerr "------ Package ${PKG} appears to be installed.  Please uninstall it."
1067     else
1068         logmsg "------ Not installed, good." 
1069     fi
1070     if logcmd $PERL32 -M$MODNAME -e '1'; then
1071         # Module is in core, don't create a package
1072         logmsg "--- Module is in core for Perl $DEPVER.  Not creating a package."
1073         exit 0
1074     else
1075         logmsg "--- Module is not in core for Perl $DEPVER.  Continuing with build."
1076     fi
1077 }
1078 
1079 #############################################################################
1080 # NPM build for node.js modules
1081 #############################################################################
1082 build_npm() {
1083     logmsg "Building module with npm"
1084     [[ -n "$NPM" ]] || NPM=/opt/omni/bin/npm
1085     pushd $TMPDIR > /dev/null
1086     if [[ -d node_modules ]]; then
1087         logcmd rm -rf node_modules
1088     fi
1089     logcmd $NPM install ${PROG}@${VER} --ws:verbose || \
1090         logerr "--- npm build failed"
1091     logmsg "Installing module into $DESTDIR"
1092     logcmd mkdir -p $DESTDIR$PREFIX/lib/node || \
1093         logerr "--- Unable to create destination directory"
1094     logcmd rsync -a node_modules/ $DESTDIR$PREFIX/lib/node/ || \
1095         logerr "--- Unable to copy files to destination directory"
1096     popd > /dev/null
1097 }
1098 
1099 #############################################################################
1100 # Scan the destination install and strip the non-stipped ELF objects
1101 #############################################################################
1102 strip_install() {
1103     logmsg "Stripping installation"
1104     pushd $DESTDIR > /dev/null || logerr "Cannot change to installation directory"
1105     while read file
1106     do
1107         if [[ "$1" = "-x" ]]; then
1108             ACTION=$(file $file | grep ELF | egrep -v "(, stripped|debugging)")
1109         else
1110             ACTION=$(file $file | grep ELF | grep "not stripped")
1111         fi
1112         if [[ -n "$ACTION" ]]; then
1113           logmsg "------ stripping $file"
1114           MODE=$(stat -c %a "$file")
1115           logcmd chmod 644 "$file" || logerr "chmod failed: $file"
1116           logcmd strip $* "$file" || logerr "strip failed: $file"
1117           logcmd chmod $MODE "$file" || logerr "chmod failed: $file"
1118         fi
1119     done < <(find . -depth -type f)
1120     popd > /dev/null
1121 }
1122 
1123 #############################################################################
1124 # Clean up and print Done message
1125 #############################################################################
1126 clean_up() {
1127     logmsg "Cleaning up"
1128     if [[ -z $DONT_REMOVE_INSTALL_DIR ]]; then
1129         logmsg "--- Removing temporary install directory $DESTDIR"
1130         logcmd chmod -R u+w $DESTDIR > /dev/null 2>&1
1131         logcmd rm -rf $DESTDIR || \
1132             logerr "Failed to remove temporary install directory"
1133         logmsg "--- Cleaning up temporary manifest and transform files"
1134         logcmd rm -f $P5M_INT $P5M_FINAL $MY_MOG_FILE || \
1135             logerr "Failed to remove temporary manifest and transform files"
1136         logmsg "Done."
1137     fi
1138 }
1139 
1140 #############################################################################
1141 # Helper function that will let you save a predefined function so you can
1142 # override it and call it later
1143 #############################################################################
1144 save_function() {
1145     local ORIG_FUNC=$(declare -f $1)
1146     local NEWNAME_FUNC="$2${ORIG_FUNC#$1}"
1147     eval "$NEWNAME_FUNC"
1148 }
1149 
1150 # Vim hints
1151 # vim:ts=4:sw=4:et: