Thursday, June 14, 2018

GNU/Solaris

Since a few months or perhaps almost a couple of years, I've been trying to learn how to manually update a few software from an ordinary Solaris 11.3 GA. That's because even if I had a contract support I wouldn't have all the latest software versions I would like to use on my Solaris system.

Of course, that's not the Solaris main goals by nowadays as currently advertised. Nevertheless, against the vision one should get another system as a front-end to Solaris, I still prefer to also use Solaris as my front-end system because I also very much appreciate all the potential that Solaris has to offer in this area as well. But for that purpose I have to manually update several software to more current versions in order to keep pace with the modern world and natural evolution. It's true that Solaris 11.4 is promising a somewhat better catch-up with this, but still it doesn't seem to be among its main goals, of course! Solaris is huge and specially fitted to big-iron SPARC systems and I totally agree with this while I'm greatly delighted I still could enjoy it on my low-end toy x86_64 system.

SAFE HARBOUR / DISCLAMER
Please, note that I mean no Licenses, Rights or Copyright infringement at all in what I post here. It's just that I appreciate so much Solaris that I'd dream of (as if it were possible) having the best of many worlds merged on a single system. For instance, I would dream of something called GNU/Solaris somewhat as presented on Linux & GNU and somewhat in the sense of GNU's What's in a Name.
Back to my manual additions and/or updates to Solaris 11.3 GA, I see that a great deal of GNU software is here and there. Even the standard distribution of Solaris 11 provides a dedicated /usr/gnu file-system (or directory) and a lot of IPS packages for that. In pursuing these manual additions and/or updates I set to a sort of building lab which I "creatively" ;-) called Stage Building.

At first, to play it safe, I was trying to keep a very strong separation for the artifacts I was attempting to build, by putting them under the /opt file-system (or directory), more specifically under /opt/gnu as well as keeping other non-GNU such stuff at /opt/usr. I've imagined these locations in a clear contrast to the usual GNU/Linux practice of adopting /usr/local as described on the Filesystem Hierarchy Standard. But with the development of my lab I have realized it should be fine and even more attractive to follow the /usr/local convention (and as a separate dedicated ZFS file-system on my building machine) as a replacement for both /opt/gnu and /opt/usr as I was using until then.

  • /opt/gnu
  • /opt/usr
       
  • /usr/local

Furthermore, I was also adopting a whole building work-space hierarchy under /stage from which I may just reconsider the source subdirectory in favor of /usr/local/src (as a separated dedicated ZFS file-system in all cases) as follows:
$ l /stage
total 31
drwxrwxrwt   4 root  root     ... build
drwxrwxrwt  10 root  root     ... package
drwxrwxrwt   4 root  root     ... prototype
drwxrwxrwt   6 root  root     ... source
drwxrwxrwt   3 root  root     ... test


One last minor revision I make is that I was initially providing both 32-bits and 64-bits ZFS file-systems for each kind of build. But it seems that, in practice, most often one is going to build just one of these bitness targets. Hence, I slightly tweak my script in order to just the required ZFS file-systems instead of always providing for both possibilities. For instance, if I set to build zlib 1.2.11, the script will have the option to create just the 32 (or 64) of the following:

$ l /stage/{build/zlib,prototype/zlib/1.2.11}
/stage/build/zlib:
total 16K
-rw-r--r--  1 ... NOTES
-rw-r--r--  1 ... setenv-1.2.11
-rw-r--r--  1 ... unsetenv
drwxr-xr-x 14 ... zlib-1.2.11
drwxr-xr-x 14 ... zlib-1.2.11-32
drwxr-xr-x 14 ... zlib-1.2.11-64

/stage/prototype/zlib/1.2.11:

total 3.0K
drwxr-xr-x 2 ... 32
drwxr-xr-x 3 ... 64



To summarize the reasons for these revisions:

  1. It seems to exist no negative impacts.
  2. It seems better integrated to the rest of the system.
  3. It seems better separated from the original /opt purpose.
  4. It's definitely more aligned with third-party software & tools.
  5. Simplification and standardization.

NOTE
The remaining assumptions from the outdated previous post Staged Building stay unchanged. For instance, at least the following delegated datasets must exist: /stage/build and /stage/prototype. At this point, /stage/package (for later IPS package creation) and specially /stage/test are optional.

My new gnu-build-preparation helper script will become:

#!/bin/bash -
 

################################################################

if [[ -z "$1" ]];
then

  echo
  echo "Usage: $0 <tarball> [<bit-target>]"

  echo
  exit 1
else
  if ! [[ -f "$1" ]];
  then

    echo
    echo "$1 not found."

    echo
    exit 1
  fi
fi


if [[ -z "$2" ]];
then
  echo "Preparing for both 32-bits and 64-bits."
  echo "Otherwise, use:
$0 \"$1\" <32 or 64>"
  echo
else
  if [[ "$2" -ne 32 ]] && [[ "$2" -ne 64 ]];
  then
    echo "Valid bit-targets: 32 or 64."
    exit 1
  fi
fi
 

################################################################

# This can be potentially better suited elsewhere
# as long as it doesn't break previous assumptions.

DS_BASE=rpool

DS_STAGE=${DS_BASE:+$DS_BASE/}stage
DS_BUILD=build
DS_PROTOTYPE=prototype


################################################################

#
# legacy gnu-coreutils IPS package lacks realpath
# TAR=$(realpath "$1")

TAR=$(readlink -f "$1") 
 
A=$(basename $1 |\
    sed 's/\(\w\+\([-.]\w\+\)*\)-.*/\1/')

V=$(basename $1 |\

    sed 's/\w\+\([-.]\w\+\)*-\([0-9]\+\([_.-][0-9]\+\)*[a-z]*\).*/\2/')

BITNESS=$2


echo
echo Processing $TAR
echo
echo ------------------------------------
echo "App: $A"
echo "Ver: $V"
echo ------------------------------------

echo
echo In the process, the following ZFS datasets will be created:

echo
echo "  $DS_
STAGE/$DS_BUILD/$A"
echo "  $DS_
STAGE/$DS_BUILD/$A/$A-$V"
if [[ -z $BITNESS ]]; then
  echo "  $DS_
STAGE/$DS_BUILD/$A/$A-$V-32"
  echo "  $DS_
STAGE/$DS_BUILD/$A/$A-$V-64"
else
  echo "  $DS_STAGE/$DS_BUILD/$A/$A-$V-$BITNESS"
fi
echo
echo "  $DS_
STAGE/$DS_PROTOTYPE/$A"
echo "  $DS_
STAGE/$DS_PROTOTYPE/$A/$A-$V"
if [[ -z $BITNESS ]]; then
  echo "  $DS_
STAGE/$DS_PROTOTYPE/$A/$A-$V/32"
  echo "  $DS_
STAGE/$DS_PROTOTYPE/$A/$A-$V/64"
else
  echo "  $DS_STAGE/$DS_PROTOTYPE/$A/$A-$V/$BITNESS"
fi
echo
read -p "Enter \"y\" to proceed: " -r
if ! [[ "$REPLY" = "y" ]] && ! [[ "$REPLY" = "Y" ]];
then
  echo "Exiting..."
  exit 1
fi 

  
################################################################

function dataset-exists()
{
  zfs list -H "$1" >/dev/null 2>&1
  local rv=$?
  echo $rv


################################################################

function get-mountpoint()
{
  echo $(zfs get -H -o value mountpoint "$1" 2>/dev/null)


################################################################

function dataset-assert-mounted()
{
  if [[ -z $(get-mountpoint "$1") ]]; then
    echo "Unexpected: \"$1\" doesn't seem to be mounted."
    exit 1
  fi
}

dataset-assert-mounted "$DS_
STAGE"
dataset-assert-mounted "$DS_
STAGE/$DS_BUILD"
dataset-assert-mounted "$DS_
STAGE/$DS_PROTOTYPE" 

################################################################

function create-source-area()
{
  if [[ $(dataset-exists "$1") -eq 1 ]]; then
    echo "Creating $1..."
    zfs create "$1"
  else
    local MP=$(get-mountpoint "$1")
    if [[ $(basename "$MP") == "$A" ]]; then
      echo "No need to create $1."
    else
      echo
      echo "$1 mounted as $MP."
      echo "No further action can be safely taken."
      echo "Exiting..."
      exit 1
    fi
  fi
}

echo

   
create-source-area "$DS_STAGE/$DS_BUILD/$A"
create-source-area "$DS_STAGE/$DS_BUILD/$A/$A-$V"
 

################################################################

echo

DS_SOURCE="$DS_
STAGE/$DS_BUILD/$A"
MP_SOURCE=$(get-mountpoint "$DS_SOURCE")

DS_SOURCE_VERSION="$DS_SOURCE/$A-$V"
MP_SOURCE_VERSION=$(get-mountpoint "$DS_SOURCE_VERSION")

if [[ $(ls -A "$MP_SOURCE_VERSION" 2>/dev/null) ]]; then
  echo
  echo "Directory $MP_SOURCE_VERSION not empty!"
  echo "No further action can be safely taken."
  echo "Exiting..."
  exit 1
fi

(cd "$MP_SOURCE_VERSION/.."; gtar xf "$TAR")

if ! [[ $(ls -A "$MP_SOURCE_VERSION" 2>/dev/null) ]]; then
  echo
  echo "Directory $MP_SOURCE_VERSION shouldn't be empty!"
  echo "No further action can be safely taken."
  echo "Exiting..."
  exit 1
fi

zfs snapshot "$DS_SOURCE_VERSION@source"
zfs set readonly=on "$DS_SOURCE_VERSION" 

  
################################################################

function set-target()
{
  local T=$1
 

  # This may be inappropriate in some cases.
  # Some "packages" require/prefer just an empty dataset.

  zfs clone "$DS_SOURCE_VERSION"@source \

            "$DS_SOURCE_VERSION-$T"
  

  # This may be too soon.
  # The "package" may need some manual fix before @start

  zfs snapshot "$DS_SOURCE_VERSION-$T"@start
}
 

if [[ -z $BITNESS ]]; then
  set-target 32
  set-target 64 

else
  set-target $BITNESS
fi

################################################################

zfs list -H -r -t all -o name "$DS_SOURCE"


################################################################

#
# Create the final resting place of the results.
# Use an independent ZFS pool/dataset, not rpool/VARSHARE.
# At end consider snapshoting, just in case.
# Could be a staging area for packaging.
#
 
 

DS_ARTIFACT="$DS_STAGE/$DS_PROTOTYPE/$A"
DS_ARTIFACT_VERSION="$DS_ARTIFACT/$V"

 
echo
echo "Creating "$DS_ARTIFACT" subtree."
echo

function create-staging-area()
{
  if [[ $(dataset-exists "$1") -eq 1 ]]; then
    zfs create -p "$1"
  fi
}

create-staging-area "$DS_ARTIFACT_VERSION"

if [[ -z $BITNESS ]]; then
  create-staging-area "$DS_ARTIFACT_VERSION/32"
  create-staging-area "$DS_ARTIFACT_VERSION/64"

else
  create-staging-area "$DS_ARTIFACT_VERSION/$BITNESS" 
fi

zfs list -H -r -t all -o name "$DS_ARTIFACT"


MP_PROTOTYPE=$(get-mountpoint "$DS_STAGE/$DS_PROTOTYPE")
 
################################################################  

echo
echo "Creating pre-configuration script."

 
cat > "$MP_SOURCE/setenv-$V" <


#
# HOSTTYPE=$HOSTTYPE
# OSTYPE=$OSTYPE
# MACHTYPE=$MACHTYPE
#
# Up to Solaris 11.3 the default is 32-bits.
# MACHTYPE is typically i386-pc-solaris2.11
#
# Since Solaris 11.4 Beta the default is 64-bits.
# MACHTYPE is x86_64-pc-solaris2.11
#
# It's advisable not to override the above env vars
# buy you experiment with the --build or --target options
# to the standard GNU's configuration script: configure.
# (they are important to properly organize the built artifacts)
#
# Bottom line it seems that the -m option to the flags
# CFLAGS, CXXFLAGS and LDFLAGS always prevail after all.
# (in terms of the actual bitness of the built artifacts)
#


if [[ -z "\$1" ]];
then
  echo "Usage: source setenv arget>
"
  return 1
else
  if [[ "\$1" -ne 32 ]] && [[ "\$1" -ne 64 ]];
  then
    echo "Valid bit-targets: 32 or 64."
    return 1
  fi
fi


# Despite setting bitness as shown next
# DON'T override HOSTTYPE and MACHTYPE
# Just remind the proper --build option


BITS=\$1

  
echo

function add-path()
{
  local COMPONENT="\$1"

  if [[ -d "\$COMPONENT" ]] ;
  then
    ! [[ "\$PATH" =~ "\$COMPONENT" ]] && \
      export PATH="\$COMPONENT:\$PATH"
  fi
}

function add-pkgconfig-path()
{
  local COMPONENT="\$1"
  local TAIL="\${PKG_CONFIG_PATH:+:\$PKG_CONFIG_PATH}"

  if [[ -d "\$COMPONENT" ]] ;
  then
    ! [[ "\$TAIL" =~ "\$COMPONENT" ]] && \
      export PKG_CONFIG_PATH="\$COMPONENT\$TAIL"
  fi
}

function extend-env()
{
  
# In this revision I'm only focusing on 64-bits.
  # Hence, I'm omitting the bitness subfolder.
  # local BASE="\$1/\$BITS" 
  local BASE="\$1"

  add-path "\$BASE/bin"
  add-pkgconfig-path "\$BASE/lib/pkgconfig"
}

add-path /usr/gnu/bin

 

#
# Other PATH and PKG_CONFIG_PATH settings.
# Put entries in reverse order of dependency.
# (following my PATH building suggestion)
# The samples below are pre-Solaris 11.4 Beta.
#

# ...
# extend-env /opt/...
 
# extend-env /usr/local
  
#
# RLE configuration
#


LIBBITS=\$([[ \$BITS -eq 32 ]] && echo "" || echo "/64")
RLEBITS=\$([[ \$BITS -eq 32 ]] && echo "" || echo "-64")
RLECFG="$MP_SOURCE/ld.config-\$BITS"


#
# /usr/local/lib is fixed for simplificity
# but could mimic the system /usr/gnu/lib and /usr/lib

# with a trailing /64 for 64-bits although this would complicate
# the 64-bits building with special prefixes for bin and lib.
#

 

crle \$RLEBITS \\
     -c "\$RLECFG" \\
     -l /usr/local/lib \\
     -l /lib\$LIBBITS:/usr/lib\$LIBBITS
 

crle \$RLEBITS -c "\$RLECFG"

export LD_CONFIG_\$BITS="\$RLECFG"
echo LD_CONFIG_\$BITS="\$RLECFG"
 

echo
 

# In general, not a good idea; evaluate.
# export CONFIG_SHELL=/bin/bash

echo CONFIG_SHELL=\$CONFIG_SHELL

echo
 

#
# The following compilers and linker overrides
# may be troublesome, specially the -m and -std options
# as it may be very source dependent; evaluate.
# The -std bar minimums on Solaris 11 Express is gnu89 and gnu++98.

# The -std bar minimums on Solaris 11.3 is gnu89 and gnu++03.
# On Solaris 11.4 Beta gnu11 and gnu++11 seem Ok.
#


FLAGS="-m\$BITS -march=core2 -mtune=core2"

export CC=/usr/bin/gcc
export CFLAGS="\$FLAGS -std=gnu89"
echo CC=\$CC CFLAGS=\$CFLAGS

echo

export CXX=/usr/bin/g++
export CXXFLAGS="\$FLAGS -std=gnu++03"
echo CXX=\$CXX CXXFLAGS=\$CXXFLAGS

echo

export LD=/usr/bin/ld

export LDFLAGS="\$FLAGS"
echo LD=\$LD LDFLAGS=\$LDFLAGS 

echo

echo PATH=\$PATH

echo

echo PKG_CONFIG_PATH=\$PKG_CONFIG_PATH

unset FLAGS
unset LIBBITS
unset RLEBITS
unset RLECFG


function get-target()
{
  case \$BITS in
  32)
    echo i386-pc-solaris2.11
    ;;
  64)
    echo x86_64-pc-solaris2.11
    ;;
  esac
 

}
 
echo
echo Suggested build sequence:
echo 
echo Fine-tune/fix/tweak/hack source...
echo 
echo \$ ./configure \\\\
echo "    "--build=\$(get-target \$BITS) \\\\
echo "    "--prefix=/usr/local \\\\
echo "    "...
echo
echo \$ gmake -j\$(( \$(psrinfo |wc -l) - 1 ))
echo 
echo \$ su
echo \# source ../setenv-$V \$BITS
echo 
echo For IPS package:
echo 
echo \# gmake DESTDIR=$MP_PROTOTYPE/$A/$V/\$BITS install
echo
echo For immediate use:
echo
echo \# gmake install  
echo \# zfs snapshot -r .../usr/local@$A-$V 

EOF

cat > "$MP_SOURCE/unsetenv-$V" <

function cleanup-env()
{

  [[ -f "\$LD_CONFIG_32" ]] && rm "\$LD_CONFIG_32"
  unset LD_CONFIG_32

  [[ -f "\$LD_CONFIG_64" ]] && rm "\$LD_CONFIG_64"
  unset LD_CONFIG_64

  unset CONFIG_SHELL

  unset CC
  unset CFLAGS

  unset CXX
  unset CXXFLAGS


  unset LD
  unset LDFLAGS

  unset PKG_CONFIG_PATH
  export PATH=/usr/bin:/usr/sbin

  unset -f add-path
  unset -f add-pkgconfig-path
  unset -f extend-env
  unset -f cleanup-env
}

cleanup-env
 

EOF

touch "$MP_SOURCE/NOTES"


And this is certainly not the end of the story.