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 a "require" 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 *require* 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         echo "depend fmri=pkg:/entire@11-$PVER type=require" >> $P5M_INT.stage1
 671     fi
 672     $PKGFMT -u < $P5M_INT.stage1 > $P5M_FINAL
 673     logmsg "--- Publishing package to $PKGSRVR"
 674     if [[ -z "$BATCH" ]]; then
 675         ask_to_continue "Last chance to sanity-check before publication! "
 676     fi
 677     if [[ -n "$DESTDIR" ]]; then
 678         logcmd $PKGSEND -s $PKGSRVR publish -d $DESTDIR -d $TMPDIR/$BUILDDIR \
 679             -d $SRCDIR -T \*.py $P5M_FINAL || \
 680             logerr "------ Failed to publish package"
 681     else
 682         # If we're a metapackage (no DESTDIR) then there are no directories to check
 683         logcmd $PKGSEND -s $PKGSRVR publish $P5M_FINAL || \
 684             logerr "------ Failed to publish package"
 685     fi
 686     logmsg "--- Published $FMRI" 
 687 }
 688 
 689 #############################################################################
 690 # Make isaexec stub binaries
 691 #############################################################################
 692 make_isa_stub() {
 693     logmsg "Making isaexec stub binaries"
 694     [[ -z $ISAEXEC_DIRS ]] && ISAEXEC_DIRS="bin sbin"
 695     for DIR in $ISAEXEC_DIRS; do
 696         if [[ -d $DESTDIR$PREFIX/$DIR ]]; then
 697             logmsg "--- $DIR"
 698             pushd $DESTDIR$PREFIX/$DIR > /dev/null
 699             make_isaexec_stub_arch $ISAPART
 700             make_isaexec_stub_arch $ISAPART64
 701             popd > /dev/null
 702         fi
 703     done
 704 }
 705 
 706 make_isaexec_stub_arch() {
 707     for file in $1/*; do
 708         [[ -f $file ]] || continue # Deals with empty dirs & non-files
 709         # Check to make sure we don't have a script
 710         read -n 5 < $file
 711         file=`echo $file | sed -e "s/$1\///;"`
 712         # Skip if we already made a stub for this file
 713         [[ -f $file ]] && continue
 714         # Only copy non-binaries if we set NOSCRIPTSTUB
 715         if [[ $REPLY != $'\177'ELF && -n "$NOSCRIPTSTUB" ]]; then
 716             logmsg "------ Non-binary file: $file - copying instead"
 717             cp $1/$file .
 718             chmod +x $file
 719             continue
 720         fi
 721         logmsg "------ $file"
 722         # Run the makeisa.sh script
 723         CC=$CC \
 724         logcmd $MYDIR/makeisa.sh $PREFIX/$DIR $file || \
 725             logerr "--- Failed to make isaexec stub for $DIR/$file"
 726     done
 727 }
 728 
 729 #############################################################################
 730 # Build commands
 731 #############################################################################
 732 # Notes:
 733 #   - These methods are designed to work in the general case.
 734 #   - You can set CFLAGS/LDFLAGS (and CFLAGS32/CFLAGS64 for arch specific flags)
 735 #   - Configure flags are set in CONFIGURE_OPTS_32 and CONFIGURE_OPTS_64 with
 736 #     defaults set in config.sh. You can append to these variables or replace
 737 #     them if the defaults don't work for you.
 738 #   - In the normal case, where you just want to add --enable-feature, set
 739 #     CONFIGURE_OPTS. This will be appended to the end of CONFIGURE_CMD
 740 #     for both 32 and 64 bit builds.
 741 #   - Any of these functions can be overriden in your build script, so if
 742 #     anything here doesn't apply to the build process for your application,
 743 #     just override that function with whatever code you need. The build
 744 #     function itself can be overriden if the build process doesn't fit into a
 745 #     configure, make, make install pattern.
 746 #############################################################################
 747 make_clean() {
 748     logmsg "--- make (dist)clean"
 749     logcmd $MAKE distclean || \
 750     logcmd $MAKE clean || \
 751         logmsg "--- *** WARNING *** make (dist)clean Failed"
 752 }
 753 
 754 configure32() {
 755     logmsg "--- configure (32-bit)"
 756     CFLAGS="$CFLAGS32 $CFLAGS" \
 757     CXXFLAGS="$CXXFLAGS32 $CXXFLAGS" \
 758     CPPFLAGS="$CPPFLAGS32 $CPPFLAGS" \
 759     LDFLAGS="$LDFLAGS32 $LDFLAGS" \
 760     CC=$CC CXX=$CXX \
 761     logcmd $CONFIGURE_CMD $CONFIGURE_OPTS_32 \
 762     $CONFIGURE_OPTS || \
 763         logerr "--- Configure failed"
 764 }
 765 
 766 configure64() {
 767     logmsg "--- configure (64-bit)"
 768     CFLAGS="$CFLAGS64 $CFLAGS" \
 769     CXXFLAGS="$CXXFLAGS64 $CXXFLAGS" \
 770     CPPFLAGS="$CPPFLAGS64 $CPPFLAGS" \
 771     LDFLAGS="$LDFLAGS64 $LDFLAGS" \
 772     CC=$CC CXX=$CXX \
 773     logcmd $CONFIGURE_CMD $CONFIGURE_OPTS_64 \
 774     $CONFIGURE_OPTS || \
 775         logerr "--- Configure failed"
 776 }
 777 
 778 make_prog() {
 779     [[ -n $NO_PARALLEL_MAKE ]] && MAKE_JOBS=""
 780     logmsg "--- make"
 781     logcmd $MAKE $MAKE_JOBS || \
 782         logerr "--- Make failed"
 783 }
 784 
 785 make_prog32() {
 786     make_prog
 787 }
 788 
 789 make_prog64() {
 790     make_prog
 791 }
 792 
 793 make_install() {
 794     logmsg "--- make install"
 795     logcmd $MAKE DESTDIR=${DESTDIR} install || \
 796         logerr "--- Make install failed"
 797 }
 798 
 799 make_install32() {
 800     make_install
 801 }
 802 
 803 make_install64() {
 804     make_install
 805 }
 806 
 807 make_pure_install() {
 808     # Make pure_install for perl modules so they don't touch perllocal.pod
 809     logmsg "--- make install (pure)"
 810     logcmd $MAKE DESTDIR=${DESTDIR} pure_install || \
 811         logerr "--- Make pure_install failed"
 812 }
 813 
 814 make_param() {
 815     logmsg "--- make $@"
 816     logcmd $MAKE "$@" || \
 817         logerr "--- $MAKE $1 failed"
 818 }
 819 
 820 # Helper function that can be called by build scripts to make in a specific dir
 821 make_in() {
 822     [[ -z $1 ]] && logerr "------ Make in dir failed - no dir specified"
 823     [[ -n $NO_PARALLEL_MAKE ]] && MAKE_JOBS=""
 824     logmsg "------ make in $1"
 825     logcmd $MAKE $MAKE_JOBS -C $1 || \
 826         logerr "------ Make in $1 failed"
 827 }
 828 
 829 # Helper function that can be called by build scripts to install in a specific
 830 # dir
 831 make_install_in() {
 832     [[ -z $1 ]] && logerr "--- Make install in dir failed - no dir specified"
 833     logmsg "------ make install in $1"
 834     logcmd $MAKE -C $1 DESTDIR=${DESTDIR} install || \
 835         logerr "------ Make install in $1 failed"
 836 }
 837 
 838 build() {
 839     if [[ $BUILDARCH == "32" || $BUILDARCH == "both" ]]; then
 840         build32
 841     fi
 842     if [[ $BUILDARCH == "64" || $BUILDARCH == "both" ]]; then
 843         build64
 844     fi
 845 }
 846 
 847 build32() {
 848     pushd $TMPDIR/$BUILDDIR > /dev/null
 849     logmsg "Building 32-bit"
 850     export ISALIST="$ISAPART"
 851     make_clean
 852     configure32
 853     make_prog32
 854     make_install32
 855     popd > /dev/null
 856     unset ISALIST
 857     export ISALIST
 858 }
 859 
 860 build64() {
 861     pushd $TMPDIR/$BUILDDIR > /dev/null
 862     logmsg "Building 64-bit"
 863     make_clean
 864     configure64
 865     make_prog64
 866     make_install64
 867     popd > /dev/null
 868 }
 869 
 870 #############################################################################
 871 # Build function for python programs
 872 #############################################################################
 873 pre_python_32() {
 874     logmsg "prepping 32bit python build"
 875 }
 876 pre_python_64() {
 877     logmsg "prepping 64bit python build"
 878 }
 879 python_build() {
 880     if [[ -z "$PYTHON" ]]; then logerr "PYTHON not set"; fi
 881     if [[ -z "$PYTHONPATH" ]]; then logerr "PYTHONPATH not set"; fi
 882     if [[ -z "$PYTHONLIB" ]]; then logerr "PYTHONLIB not set"; fi
 883     logmsg "Building using python setup.py"
 884     pushd $TMPDIR/$BUILDDIR > /dev/null
 885 
 886     if [[ $BUILDARCH == "32" || $BUILDARCH == "both" ]]; then
 887         buildpython32
 888     fi
 889     if [[ $BUILDARCH == "64" || $BUILDARCH == "both" ]]; then
 890         buildpython64
 891     fi
 892     popd > /dev/null
 893 }
 894 
 895 buildpython32() {
 896     ISALIST=i386
 897     export ISALIST
 898     pre_python_32
 899     logmsg "--- setup.py (32) build"
 900     CFLAGS="$CFLAGS32 $CFLAGS" \
 901     CXXFLAGS="$CXXFLAGS32 $CXXFLAGS" \
 902     CPPFLAGS="$CPPFLAGS32 $CPPFLAGS" \
 903     LDFLAGS="$LDFLAGS32 $LDFLAGS" \
 904     CC=$CC CXX=$CXX \
 905     logcmd $PYTHON ./setup.py build ||
 906         logerr "--- build failed"
 907     logmsg "--- setup.py (32) install"
 908     logcmd $PYTHON \
 909         ./setup.py install --root=$DESTDIR ||
 910         logerr "--- install failed"
 911 }
 912 
 913 buildpython64(){
 914     ISALIST="amd64 i386"
 915     export ISALIST
 916     pre_python_64
 917     logmsg "--- setup.py (64) build"
 918     CFLAGS="$CFLAGS64 $CFLAGS" \
 919     CXXFLAGS="$CXXFLAGS64 $CXXFLAGS" \
 920     CPPFLAGS="$CPPFLAGS64 $CPPFLAGS" \
 921     LDFLAGS="$LDFLAGS64 $LDFLAGS" \
 922     CC=$CC CXX=$CXX \
 923     logcmd $PYTHON ./setup.py build ||
 924         logerr "--- build failed"
 925     logmsg "--- setup.py (64) install"
 926     logcmd $PYTHON \
 927         ./setup.py install --root=$DESTDIR ||
 928         logerr "--- install failed"
 929 }
 930 
 931 #############################################################################
 932 # Build/test function for perl modules
 933 #############################################################################
 934 # Detects whether to use Build.PL or Makefile.PL
 935 # Note: Build.PL probably needs Module::Build installed
 936 #############################################################################
 937 vendorizeperl() {
 938     logcmd mv $DESTDIR/usr/perl5/lib/site_perl $DESTDIR/usr/perl5/vendor_perl || logerr "can't move to vendor_perl"
 939     logcmd mkdir -p $DESTDIR/usr/perl5/${DEPVER}
 940     logcmd mv $DESTDIR/usr/perl5/man $DESTDIR/usr/perl5/${DEPVER}/man || logerr "can't move perl man"
 941 }
 942 
 943 buildperl() {
 944     if [[ -f $SRCDIR/${PROG}-${VER}.env ]]; then
 945         logmsg "Sourcing environment file: $SRCDIR/${PROG}-${VER}.env"
 946         source $SRCDIR/${PROG}-${VER}.env
 947     fi
 948     if [[ $BUILDARCH == "32" || $BUILDARCH == "both" ]]; then
 949         buildperl32
 950     fi
 951     if [[ $BUILDARCH == "64" || $BUILDARCH == "both" ]]; then
 952         buildperl64
 953     fi
 954 }
 955 
 956 buildperl32() {
 957     if [[ -f $SRCDIR/${PROG}-${VER}.env32 ]]; then
 958         logmsg "Sourcing environment file: $SRCDIR/${PROG}-${VER}.env32"
 959         source $SRCDIR/${PROG}-${VER}.env32
 960     fi
 961     pushd $TMPDIR/$BUILDDIR > /dev/null
 962     logmsg "Building 32-bit"
 963     export ISALIST="$ISAPART"
 964     local OPTS
 965     OPTS=${MAKEFILE_OPTS//_ARCH_/}
 966     OPTS=${OPTS//_ARCHBIN_/$ISAPART}
 967     if [[ -f Makefile.PL ]]; then
 968         make_clean
 969         makefilepl32 $OPTS
 970         make_prog
 971         [[ -n $PERL_MAKE_TEST ]] && make_param test
 972         make_pure_install
 973     elif [[ -f Build.PL ]]; then
 974         build_clean
 975         buildpl32 $OPTS
 976         build_prog
 977         [[ -n $PERL_MAKE_TEST ]] && build_test
 978         build_install
 979     fi
 980     popd > /dev/null
 981     unset ISALIST
 982     export ISALIST
 983 }
 984 
 985 buildperl64() {
 986     if [[ -f $SRCDIR/${PROG}-${VER}.env64 ]]; then
 987         logmsg "Sourcing environment file: $SRCDIR/${PROG}-${VER}.env64"
 988         source $SRCDIR/${PROG}-${VER}.env64
 989     fi
 990     pushd $TMPDIR/$BUILDDIR > /dev/null
 991     logmsg "Building 64-bit"
 992     local OPTS
 993     OPTS=${MAKEFILE_OPTS//_ARCH_/$ISAPART64}
 994     OPTS=${OPTS//_ARCHBIN_/$ISAPART64}
 995     if [[ -f Makefile.PL ]]; then
 996         make_clean
 997         makefilepl64 $OPTS
 998         make_prog
 999         [[ -n $PERL_MAKE_TEST ]] && make_param test
1000         make_pure_install
1001     elif [[ -f Build.PL ]]; then
1002         build_clean
1003         buildpl64 $OPTS
1004         build_prog
1005         [[ -n $PERL_MAKE_TEST ]] && build_test
1006         build_install
1007     fi
1008     popd > /dev/null
1009 }
1010 
1011 makefilepl32() {
1012     logmsg "--- Makefile.PL 32-bit"
1013     logcmd $PERL32 Makefile.PL PREFIX=$PREFIX $@ ||
1014         logerr "Failed to run Makefile.PL"
1015 }
1016 
1017 makefilepl64() {
1018     logmsg "--- Makefile.PL 64-bit"
1019     logcmd $PERL64 Makefile.PL PREFIX=$PREFIX $@ ||
1020         logerr "Failed to run Makefile.PL"
1021 }
1022 
1023 buildpl32() {
1024     logmsg "--- Build.PL 32-bit"
1025     logcmd $PERL32 Build.PL prefix=$PREFIX $@ ||
1026         logerr "Failed to run Build.PL"
1027 }
1028 
1029 buildpl64() {
1030     logmsg "--- Build.PL 64-bit"
1031     logcmd $PERL64 Build.PL prefix=$PREFIX $@ ||
1032         logerr "Failed to run Build.PL"
1033 }
1034 
1035 build_clean() {
1036     logmsg "--- Build (dist)clean"
1037     logcmd ./Build distclean || \
1038     logcmd ./Build clean || \
1039         logmsg "--- *** WARNING *** make (dist)clean Failed"
1040 }
1041 
1042 build_prog() {
1043     logmsg "--- Build"
1044     logcmd ./Build ||
1045         logerr "Build failed"
1046 }
1047 
1048 build_test() {
1049     logmsg "--- Build test"
1050     logcmd ./Build test ||
1051         logerr "Build test failed"
1052 }
1053 
1054 build_install() {
1055     logmsg "--- Build install"
1056     logcmd ./Build pure_install --destdir=$DESTDIR || \
1057         logmsg "Build install failed"
1058 }
1059 
1060 test_if_core() {
1061     logmsg "Testing whether $MODNAME is in core"
1062     logmsg "--- Ensuring ${PKG} is not installed"
1063     if logcmd pkg info ${PKG}; then
1064         logerr "------ Package ${PKG} appears to be installed.  Please uninstall it."
1065     else
1066         logmsg "------ Not installed, good." 
1067     fi
1068     if logcmd $PERL32 -M$MODNAME -e '1'; then
1069         # Module is in core, don't create a package
1070         logmsg "--- Module is in core for Perl $DEPVER.  Not creating a package."
1071         exit 0
1072     else
1073         logmsg "--- Module is not in core for Perl $DEPVER.  Continuing with build."
1074     fi
1075 }
1076 
1077 #############################################################################
1078 # NPM build for node.js modules
1079 #############################################################################
1080 build_npm() {
1081     logmsg "Building module with npm"
1082     [[ -n "$NPM" ]] || NPM=/opt/omni/bin/npm
1083     pushd $TMPDIR > /dev/null
1084     if [[ -d node_modules ]]; then
1085         logcmd rm -rf node_modules
1086     fi
1087     logcmd $NPM install ${PROG}@${VER} --ws:verbose || \
1088         logerr "--- npm build failed"
1089     logmsg "Installing module into $DESTDIR"
1090     logcmd mkdir -p $DESTDIR$PREFIX/lib/node || \
1091         logerr "--- Unable to create destination directory"
1092     logcmd rsync -a node_modules/ $DESTDIR$PREFIX/lib/node/ || \
1093         logerr "--- Unable to copy files to destination directory"
1094     popd > /dev/null
1095 }
1096 
1097 #############################################################################
1098 # Scan the destination install and strip the non-stipped ELF objects
1099 #############################################################################
1100 strip_install() {
1101     logmsg "Stripping installation"
1102     pushd $DESTDIR > /dev/null || logerr "Cannot change to installation directory"
1103     while read file
1104     do
1105         if [[ "$1" = "-x" ]]; then
1106             ACTION=$(file $file | grep ELF | egrep -v "(, stripped|debugging)")
1107         else
1108             ACTION=$(file $file | grep ELF | grep "not stripped")
1109         fi
1110         if [[ -n "$ACTION" ]]; then
1111           logmsg "------ stripping $file"
1112           MODE=$(stat -c %a "$file")
1113           logcmd chmod 644 "$file" || logerr "chmod failed: $file"
1114           logcmd strip $* "$file" || logerr "strip failed: $file"
1115           logcmd chmod $MODE "$file" || logerr "chmod failed: $file"
1116         fi
1117     done < <(find . -depth -type f)
1118     popd > /dev/null
1119 }
1120 
1121 #############################################################################
1122 # Clean up and print Done message
1123 #############################################################################
1124 clean_up() {
1125     logmsg "Cleaning up"
1126     if [[ -z $DONT_REMOVE_INSTALL_DIR ]]; then
1127         logmsg "--- Removing temporary install directory $DESTDIR"
1128         logcmd chmod -R u+w $DESTDIR > /dev/null 2>&1
1129         logcmd rm -rf $DESTDIR || \
1130             logerr "Failed to remove temporary install directory"
1131         logmsg "--- Cleaning up temporary manifest and transform files"
1132         logcmd rm -f $P5M_INT $P5M_FINAL $MY_MOG_FILE || \
1133             logerr "Failed to remove temporary manifest and transform files"
1134         logmsg "Done."
1135     fi
1136 }
1137 
1138 #############################################################################
1139 # Helper function that will let you save a predefined function so you can
1140 # override it and call it later
1141 #############################################################################
1142 save_function() {
1143     local ORIG_FUNC=$(declare -f $1)
1144     local NEWNAME_FUNC="$2${ORIG_FUNC#$1}"
1145     eval "$NEWNAME_FUNC"
1146 }
1147 
1148 # Vim hints
1149 # vim:ts=4:sw=4:et: