I’m currently building Solaris IPS packages for Some Ruby gems. Yes, I know rubygems does a very good job of keeping everything together. But that does not work so well on backend systems where you don’t have a free internet access and where repeatability is important. So I decided to wrap the gems we need into IPS packages.

The IPS pkg* tools work pretty well when they try to find dependencies between different packages automatically. But so far, they claim to do it only for ELF binaries and Python files. So for Ruby, I would either have to specify dependencies manually (and miss half of them) or try to find a way to inspect the Ruby files as well.

So I tried to get some bash lines together to do what I wanted;-) They will probably not be complete yet!! But so far, they work pretty well. I also found, that the pkgdepend does not really handle the #!/usr/bin/env ruby code found in some scripts, so I tried to fix that as well.

So for the old Mongrel gem, the following dependencies were automatically created;-) I think that’s pretty complete.

depend fmri=pkg:/my_prefix/ruby/gem/cgi_multipart_eof_fix@2.5.0-0.1 type=require
depend fmri=pkg:/my_prefix/ruby/gem/daemons@1.1.6-0.1 type=require
depend fmri=pkg:/my_prefix/ruby/gem/fastthread@1.0.7-0.1 type=require
depend fmri=pkg:/my_prefix/ruby/gem/gem_plugin@0.2.3-0.1 type=require
depend fmri=pkg:/my_prefix/ruby/rubygems@1.6.2-0.1 type=require
depend fmri=pkg:/my_prefix/ruby@1.8.7.357-0.1 type=require
depend fmri=pkg:/system/library/math@0.5.11-0.174.0.0.0.0.0 type=require
depend fmri=pkg:/system/library@0.5.11-0.175.0.2.0.3.1 type=require
depend fmri=pkg:/system/linker@0.5.11-0.175.0.2.0.2.1 type=require

Most IPS guides specify to run pkgdepend generate and then pkgdepend resolve, the following code would go right in between those two steps.

pkgdepend generate -md $DESTDIR/dist/ $DESTDIR/pkg/$SW.p5m.2 | \
        pkgfmt > $DESTDIR/pkg/$SW.p5m.3

# XXX handle the usr/bin/env ruby case
dest_prefix=`echo $DEST | sed -e 's:^/::'`
mv $DESTDIR/pkg/$SW.p5m.3 $DESTDIR/pkg/$SW.p5m.3.orig
awk -F= -e "
        BEGIN {
                e = 0;
        }
        \$4 ~ /^env / {
                e = 1;
                printf \"%s=%s=%s=ruby \\\\\n\", \$1, \$2, \$3;
                next;
        }
        e == 1 && \$2 ~ /^usr\/bin / {
                printf \"%s=$dest_prefix/bin \\\\\n\", \$1;
                e = 0;
                next;
        }
        {
                e = 0;
                print \$0;
        }" < $DESTDIR/pkg/$SW.p5m.3.orig > $DESTDIR/pkg/$SW.p5m.3

for i in `find $DESTDIR/dist$DEST/lib/ruby/gems/1.8/gems/*/lib/ -name '*.rb' `
do
  i_path=`echo $i | sed -e "s:$DESTDIR/dist/::"`
  i_reqs=`awk '$1 == "require" { print $2; }' < $i | tr -d "'" | tr -d '"' | sort -u`
  for k in $i_reqs
  do
    x=""
    y=""
    x=`ls $DESTDIR/dist$DEST/lib/ruby/gems/1.8/gems/*/lib/$k.rb 2>/dev/null`
    if [ -z "$x" ]
    then
      # XXX might not find the best version?
      y=`ls -r $DEST/lib/ruby/gems/1.8/gems/*/lib/$k.rb 2>/dev/null | head -1`
    fi
    if [ -z "$x" -a -z "$y" ]
    then
      y=`ls $DEST/lib/ruby/1.*/$k.rb 2>/dev/null`
    fi
    if [ -z "$x" -a -z "$y" ]
    then
      y=`ls $DEST/lib/ruby/vendor_ruby/$k.rb 2>/dev/null`
    fi
    if [ -z "$x" -a -z "$y" ]
    then
      y=`ls $DEST/lib/ruby/site_ruby/$k.rb 2>/dev/null`
    fi

    if [ -n "$y" ]
    then
      echo "depend type=require fmri=__TBD pkg.debug.depend.file="`basename $y`" \\"
      echo "    pkg.debug.depend.path="`dirname $y | sed -e 's:^/::'`" \\"
      echo "    pkg.debug.depend.reason=$i_path pkg.debug.depend.type=script"
    fi
  done
done >> $DESTDIR/pkg/$SW.p5m.3

i=`ls $DESTDIR/dist$DEST/lib/ruby/gems/1.8/specifications/*.gemspec 2>/dev/null`
if [ -n "$i" ]
then
  for k in $i
  do
    echo "depend type=require fmri=__TBD pkg.debug.depend.file=gem \\"
    echo "    pkg.debug.depend.path="`echo $DEST/bin | sed -e 's:^/::'`" \\"
    echo "    pkg.debug.depend.reason="`echo $k | sed -e "s:$DESTDIR/dist/::"`

    dev_deps=`fgrep 'add_development_dependency(' < $k | \
        sed -e 's:^.*add_development_dependency(%q<\([^>]*\)>.*$:\1:' | \
        sort -u`
    deps=`fgrep 'add_dependency(' < $k | \
        sed -e 's:^.*add_dependency(%q<\([^>]*\)>.*$:\1:' | \
        sort -u`
    for l in $deps
    do
      found=0
      for m in $dev_deps
      do
        if [ "$l" = "$m" ]
        then
          found=1
        fi
      done

      if [ $found -eq 0 ]
      then
        spec=`ls -r $DEST/lib/ruby/gems/1.8/specifications/$l-*.gemspec | head -1`
        if [ -n "$spec" ]
        then
          echo "depend type=require fmri=__TBD pkg.debug.depend.file="`basename $spec`" \\"
          echo "    pkg.debug.depend.path="`dirname $spec | sed -e 's:^/::'`" \\"
          echo "    pkg.debug.depend.reason="`echo $k | sed -e "s:$DESTDIR/dist/::"`
        fi
      fi
    done
  done
fi >> $DESTDIR/pkg/$SW.p5m.3

pkgdepend resolve -m $DESTDIR/pkg/$SW.p5m.3