More work in progress.
[distorted-chroot] / bin / update-cross-tools
diff --git a/bin/update-cross-tools b/bin/update-cross-tools
new file mode 100755 (executable)
index 0000000..a738af0
--- /dev/null
@@ -0,0 +1,277 @@
+#! /bin/sh -e
+###
+### Fetch native versions of tools for insertion into foreign chroots
+###
+### (c) 2018 Mark Wooding
+###
+
+###----- Licensing notice ---------------------------------------------------
+###
+### This file is part of the distorted.org.uk chroot maintenance tools.
+###
+### distorted-chroot is free software: you can redistribute it and/or
+### modify it under the terms of the GNU General Public License as
+### published by the Free Software Foundation; either version 2 of the
+### License, or (at your option) any later version.
+###
+### distorted-chroot is distributed in the hope that it will be useful,
+### but WITHOUT ANY WARRANTY; without even the implied warranty of
+### MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+### General Public License for more details.
+###
+### You should have received a copy of the GNU General Public License
+### along with distorted-chroot.  If not, write to the Free Software
+### Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+### USA.
+
+. state/config.sh # @@@config@@@
+
+###--------------------------------------------------------------------------
+### Utilities.
+
+chase_link () {
+  p=$1 d=
+  ## Copy an absolute path P from the donor tree `$root/' into the
+  ## cross-tools tree `$crossnew/'.  If resolving P involves traversing a
+  ## symbolic link, then ensure that the pieces of filesystem it directs us
+  ## to are also copied.
+
+  ## Work through the remaining components of the path.
+  while :; do
+
+    ## Analyse the first remaining component of the path P.
+    case $p in
+
+      ## It's empty.  We're done.
+      "") break ;;
+
+      ## A redundant `/' or `./'.  Skip it.
+      "/"*) p=${p#/} ;;
+      "./"*) p=${p#./} ;;
+
+      ## A `../'.  Strip off the trailing component of D.
+      "../"*)
+       p=${p#../}
+       case $d in */*) d=${d%/*} ;; *) d= ;; esac
+       ;;
+
+      ## Something else.  Transfer the component name to D.
+      *)
+       case $p in */*) f=${p%%/*} p=${p#*/} ;; *) f=$p p="" ;; esac
+       d=${d:+$d/}$f
+       ;;
+    esac
+
+    ## If D doesn't refer to a file in the cross-tools tree, then maybe it
+    ## refers to something in the donor tree.  Find out what, and copy it
+    ## into the cross-tools tree.
+    if ! [ -e "$crossnew$d" ] && ! [ -L "$crossnew$d" ]; then
+      if [ -d "$root/$d" ] && ! [ -L "$root/$d" ]; then
+       mkdir "$crossnew$d"
+      else
+       echo >&2 "$0: copy /$d to satisfy symlinks"
+       rsync -aHR $root/./$d $crossnew
+      fi
+    fi
+
+    ## If D refers to a symbolic link, then append the link target to P, so
+    ## that we can make sure we copy the target.
+    if [ -L "$crossnew$d" ]; then
+      t=$(readlink "$crossnew$d")
+      case $t in /*) t=${t#/} d= ;; esac
+      case $d in */*) d=${d%/*} ;; *) d= ;; esac
+      p=$t${p:+/$p}
+    fi
+  done
+}
+
+###--------------------------------------------------------------------------
+### Main program.
+
+## Parse the command line.
+badp=nil
+case $# in 2) ;; *) badp=t ;; esac
+case $badp in t) echo >&2 "usage: $0 DIST MYARCH"; exit 2 ;; esac
+d=$1 myarch=$2
+
+## Keep track of our original stdout.
+exec 3>&1
+
+## Figure out derived architecture names.
+mymulti=$(dpkg-architecture -a$myarch -qDEB_HOST_MULTIARCH)
+
+## First, set `cross_archs' as a list of GNUish names for our supported
+## foreign architectures.
+cross_archs="arm-linux-gnueabi arm-linux-gnueabihf aarch64-linux-gnu"
+
+## Make a list of extra packages we'll need to install to obtain our tools.
+cross_pkgs="
+       apt bash ccache coreutils dash eatmydata fakeroot findutils
+       gnupg gpgv gzip m4 make mawk qemu-user-static sed tar xz-utils"
+for a in $cross_archs; do
+  for i in gcc g++ binutils; do
+    cross_pkgs="$cross_pkgs $i-$a"
+  done
+done
+cross_pkgs=$(echo $cross_pkgs)
+
+## Make an enormous shopping list of paths.
+##
+## The `wanted' list consists of two kinds of items: an absolute path names a
+## prefix (not necessarily a directory name) to be attached to the following
+## relative names, up to the end of the list or the next absolute path.
+wanted="
+       /usr/bin/ apt apt-cache apt-config apt-get apt-key apt-mark
+       /usr/lib/apt/ methods/ solvers/
+
+       /bin/ cat chgrp chown cp date dd df dir echo false ln ls mkdir
+               mknod mktemp mv pwd readlink rm rmdir sleep stty sync touch
+               true uname vdir
+       /usr/bin/ [ arch b2sum base32 base64 basename chcon cksum comm
+               csplit cut dircolors dirname du env expand expr factor fmt
+               fold groups head hostid id install join link logname md5sum
+               mkfifo nice nl nohup nproc numfmt od paste pathchk pinky pr
+               printenv printf ptx realpath runcon seq sha1sum sha224sum
+               sha256sum sha384sum sha512sum shred shuf sort split stat
+               stdbuf sum tac tail tee test timeout tr truncate tsort tty
+               unexpand uniq unlink users wc who whoami yes
+       /usr/lib/$mymulti/ coreutils/
+
+       /lib/$mymulti/ libnss_*.so.*
+
+       /usr/bin/ gpg gpgv gpgconf kbxutil watchgnupg
+
+       /usr/bin/ qemu-*-static
+
+       /bin/ bash dash gzip sed tar
+       /usr/bin/ ccache find m4 make mawk xargs xz
+       /usr/lib/$mymulti/ libeatmydata.so* libfakeroot/
+
+       /etc/ld.so.conf.d/ $mymulti.conf fakeroot*.conf"
+
+for a in $cross_archs; do
+  wanted="$wanted
+
+       /usr/bin/$a- addr2line ar as c++filt dwp elfedit gprof ld ld.*
+               nm objcopy objdump ranlib readelf size strings strip
+
+       /usr/bin/$a- cpp gcc g++ gcov gcov-dump gcov-tool gprof
+               gcc-ar gcc-nm gcc-ranlib
+       /usr/lib/gcc-cross/$a/ ..."
+done
+wanted=$(echo $wanted)
+
+## Figure out how to recognize dynamic executables.
+case $myarch in
+  i386) elfsig=7f454c46010101??0000000000000000????0300 ;;
+  amd64) elfsig=7f454c46020101??0000000000000000????3e00 ;;
+  *) echo >&2 "$0: unsupported local arch \`$myarch'"; exit 2 ;;
+esac
+
+## Open a session to the donor chroot.
+echo >&2 "$0: create $d snapshot"
+sess=$(schroot -bc$LVPREFIX$d-$myarch 3>&-)
+
+## Make sure the donor tree is up-to-date, and install the extra packages we
+## need.
+schroot -uroot -rc$sess -- eatmydata sh -ec "
+       apt-get update
+       apt-get -y upgrade
+       apt-get -y install $cross_pkgs"
+
+## Establish some pathnames.  Prepare a place for our cross-tools tree.
+crossdir=$LOCAL/cross/$d-$myarch/
+crossold=${crossdir%/}.old/ crossnew=${crossdir%/}.new/
+root=/schroot/$sess/fs
+rm -rf $crossnew; mkdir -p $crossnew
+
+## Work through the shopping list, copying the things it names into the
+## cross-tools tree.
+dir=/
+for i in $wanted; do
+  case $i in
+    /*)
+      dir=$i
+      ;;
+    *)
+      case $i in ...) f=$dir ;; *) f=$dir$i ;; esac
+      echo >&2 "$0: copy $f"
+      rsync -aHR $root/.$f $crossnew
+      ;;
+  esac
+done
+
+## Chase links in the new tree, copying extra stuff that we'll need.
+find $crossnew -xtype l -print | while read i; do
+  chase_link ${i#$crossnew}
+done
+
+## Search the new tree for ELF binaries, and build a list of them in
+## `QUEUE.in'.
+find $crossnew -type f -print | while read i; do
+  sig=$(head -c20 "$i" | bincode -e -m0 -flowerc hex)
+  case $sig in $elfsig) echo "$i" ;; esac
+done >$root/private/QUEUE.in
+
+while [ -s $root/private/QUEUE.in ]; do
+  ## Work through the ELF binaries in `QUEUE.in', determining which shared
+  ## libraries they'll need.  Write the list of dependencies to `QUEUE.out'
+  schroot -uroot -rc$sess -- eatmydata sh -ec '
+       prog=$1
+       while read i; do
+       echo >&2 "$prog: scanning binary $i"
+         ldd "$i" | while read a b c d; do
+           case $a:$b:$c:$d in
+             not:a:dynamic:executable) ;;
+             statically:linked::) ;;
+             /*) echo "$a" ;;
+             *:=\>:/*) echo "$c" ;;
+             linux-*) ;;
+             *) echo >&2 "$i: unable to find $a"; exit 2 ;;
+           esac
+         done
+       done </private/QUEUE.in >/private/QUEUE.out' - "$0"
+
+  ## Work through the shared libraries in `QUEUE.out', copying them to the
+  ## cross-tools tree if they're not there already.  Add the new ones to a
+  ## new `QUEUE.in' file to scan them in turn.
+  while read i; do
+    if [ -e "$crossnew$i" ] || [ -L "$crossnew$i" ]
+    then continue; fi
+    if [ -d "$root$i" ]; then continue; fi
+    echo >&2 "$0: copy $i"
+    rsync -aHR $root/.$i $crossnew >&3
+    chase_link $i >&3
+    sig=$(head -c20 $crossnew$i | bincode -e -m0 -flowerc hex)
+    case $sig in $elfsig) echo "$i" ;; esac
+  done <$root/private/QUEUE.out >$root/private/QUEUE.in
+done
+
+## Set up the cross-compiler.  This is rather hairy.
+echo >&2 "$0: establish TOOLCHAIN"
+for a in $cross_archs; do
+  tooldir=$crossnew/TOOLCHAIN/$a
+  mkdir -p $tooldir
+  for i in $crossnew/usr/bin/$a-*; do
+    t=${i#$crossnew/usr/bin/}
+    mv $i $tooldir/$t
+    ln -s $t $tooldir/${t#$a-}
+  done
+done
+mkdir $crossnew/TOOLCHAIN/lib
+ln -s ../../usr/lib/gcc-cross $crossnew/TOOLCHAIN/lib/
+
+## Set up the emulator.
+echo >&2 "$0: establish QEMU"
+mkdir $crossnew/QEMU
+mv $crossnew/usr/bin/qemu-*-static $crossnew/QEMU/
+
+## We're done.  Remove the snapshot, and replace the old cross-tools tree
+## with our new one.
+echo >&2 "$0: remove snapshot"
+schroot -ec$sess 3>&-
+if [ -d $crossdir ]; then mv $crossdir $crossold; fi
+mv $crossnew $crossdir; rm -rf $crossold
+echo >&2 "$0: committed $crossdir"
+
+###----- That's all, folkd --------------------------------------------------