#!/usr/bin/bash -e # djh-reinstall.sh # created by Doug Henderson # inspired by similar code posted on stackoverflow and the cygwin mailing list # updated 2020-05-26 by Doug Henderson # updated 2023-11-23 by Doug Henderson # *** USE AT YOUR OWN RISK *** # djh-reinstall.sh determines a minimal set of packages which will reinstall # an existing cygwin instance. # # You can use the generated script to # 1. perform clean reinstall with the same set of packages as a current # instance. # 2. clone an existing install to another instance on this or another # machine. # # It uses the cygcheck-dep script from the cygcheck-dep package to # determine a set of packages which have no dependent packages. # It relies on cygwin setup to compute all dependencies. # You must change the variables in the configuration section to your requirements. arch=$( uname -m) if [ $arch = "x86" ] ; then bits=32 elif [ $arch = "x86_64" ] ; then bits=64 else echo "Failed to determine bits for $arch" exit 1 fi ################################ # start of configuration section # You must change the variables in this section to your requirements. # SETUP_DIR contains the Cygwin setup executable # This where you saved the installer from https://www/cygwin.com SETUP_DIR_W="D:\\Users\\Doug\\Down_Loads\\cygwin" # ROOT is the windows directory which will be the cygwin root directory. # All of cygwin is installed below this directory. # Select the 1st following line to install to same dir. # select the 2nd following line for the recommended root directory. # I recommend C:\cygwin32 and C:\cygwin64 for 32 bit and 64 bit installs. # ROOT_W="$( cygpath -wa / )" ROOT_W="C:\\cygwin${bits}" # CACHE is the windows directory containing the local package cache # You can use one directory, or one for each architecture. # It is strongly recommended that you use one for each architecture. # This folder must not be under the ROOT folder. CACHE_W="D:\\Users\\Doug\\Down_Loads\\cygwin${bits}cache" # MIRROR is the URL of a Cygwin mirror site. Pick your favourite, or fastest. # Use the same one as you select when running the installer: setup_*.exe MIRROR="http://mirrors.kernel.org/sourceware/cygwin/" # end of configuration section ############################## # remove scatch files rm -f /tmp/djh-check.* USAGE="Usage: $0 [--incomplete | -I] [--long | -L] [--overlay DIR | -O DIR] [--pause | -P] --incomplete, -I - reinstall incomplete packages only --long, -L - use setup's long instead of short form options --overlay DIR, -O DIR - use DIR as an overlay package server --pause, -P - add a pause at end of command script --verbose, -v - verbose, show some extra info --debug, -D - debug, show lots of extra info --help, -h - display usage message and exit WARNING: reinstalling incomplete python packages will downgrade those python packages that were upgraded by using 'pip install -U PKG'. " INCOMPLETE= LONG= OVERLAY= PAUSE= VERBOSE= VERSION= DEBUG= die() { echo "$*" >&2 ; exit 2 ; } # complain to STDERR and exit with error needs_arg() { if [ -z "$OPTARG" ] ; then die "No arg for --$OPT option" ; fi ; } while getopts "hvDILO:PV-:" OPT ; do # echo "*1st: OPT=$OPT OPTIND=$OPTIND NAME=$NAME OPTARG=$OPTARG" if [ "$OPT" = "-" ]; then # long option: reformulate OPT and OPTARG OPT="${OPTARG%%=*}" # extract long option name OPTARG="${OPTARG#$OPT}" # extract long option argument (may be empty) OPTARG="${OPTARG#=}" # if long option argument, remove assigning `=` # echo "*2nd: OPT=$OPT OPTIND=$OPTIND NAME=$NAME OPTARG=$OPTARG" fi case $OPT in h | help ) echo "$USAGE" ; exit 0 ;; v | verbose ) VERBOSE=true ;; D | debug ) DEBUG=true ;; I | incomplete ) INCOMPLETE=true ;; L | long ) LONG=true ;; O | overlay ) needs_arg; OVERLAY=$OPTARG ;; P | pause ) PAUSE=true ;; V | version ) VERSION=true ;; ??* ) die "Illegal option --$OPT" ;; # bad long option \?) exit 2 ;; # bad short option (error reported via getopts) *) echo "*WTF: OPT=$OPT OPTIND=$OPTIND NAME=$NAME OPTARG=$OPTARG" exit 1 ;; esac done shift $((OPTIND-1)) # remove parsed options and args from $@ list if [ -n "$DEBUG" ] ; then echo "Options: INCOMPLETE=$INCOMPLETE LONG=$LONG OVERLAY=$OVERLAY" echo " PAUSE=$PAUSE VERBOSE=$VERBOSE DEBUG=$DEBUG" echo "" fi # precompute some variables # SETUP_EXE is the architecture dependent Cygwin setup executable. SETUP_EXE="setup-$(arch).exe" # SETUP_PATH_W is the full windows path to the Cygwin setup executable. SETUP_PATH_W="${SETUP_DIR_W}\\${SETUP_EXE}" # CMD_W is the full windows path to the reinstall command we are creating. CMD_W="${SETUP_DIR_W}\\djh-reinstall${bits}.cmd" # CMD_U is the full cygwin path to the reinstall command we are creating. CMD_U="$( cygpath -u ${CMD_W} )" # show the configuration variables show_vars() { EFMT="INFO: %-14s = %-60s # %s\n" printf "${EFMT}" SETUP_DIR_W ${SETUP_DIR_W} "windows dir containing setup exe" printf "${EFMT}" ROOT_W ${ROOT_W} "windows path of cygwin root" printf "${EFMT}" CACHE_W ${CACHE_W} "setup exe cache directory" printf "${EFMT}" MIRROR ${MIRROR} "URL of preferred mirror" if true ; then echo "" printf "${EFMT}" reinstall.sh ${0} "this script" printf "${EFMT}" arch ${arch} "architecture name X86 or X86_64" printf "${EFMT}" bits ${bits} "architecture bits 32 or 64" printf "${EFMT}" SETUP_EXE ${SETUP_EXE} "setup executable name" printf "${EFMT}" SETUP_PATH_W ${SETUP_PATH_W} "windows path to setup executable" printf "${EFMT}" CMD_W ${CMD_W} "windows path to output cmd script" printf "${EFMT}" CMD_U ${CMD_U} "unix path to output cmd script" fi } if [ -n "$VERBOSE" ] || [ -n "$DEBUG" ] ; then show_vars fi # check that required programs are installed check_dep() { if [ ! -x $( which $1 > /dev/null 2>&1 ) ] ; then echo >&2 "$1 : command not found" pkg=$( cygcheck -f /usr/bin/$1 ) echo >&2 "Please install $pkg which provides $1" exit 1 fi } check_dep cygcheck-dep check_dep unix2dos.exe if [ ! -f $( cygpath -u "${SETUP_PATH_W}" ) ] ; then echo >&2 "WARNING: setup not found: $( cygpath -u ${SETUP_PATH_W} )" echo >&2 "WARNING: ${SETUP_EXE} must be installed to run the generated script" fi FMT="INFO: %-24s shows %4d packages\n" if [ -n "$INCOMPLETE" ] ; then echo "WARNING: This resets all Python packages upgraded using 'pip install -U PKG'." cygcheck -c | grep -a "Incomplete" >> /tmp/djh-check.p2.log # our sed script does: # remove everything after the first space to end of line. # delete line(s) starting with '_' (just in case). sed < /tmp/djh-check.p2.log \ -e 's/ .*$//' \ -e '/^_/d' \ > /tmp/djh-check.p2a.log p2=( $( sort --unique /tmp/djh-check.p2a.log ) ) printf "${FMT}" "cgcheck -c Incomp" ${#p2[*]} else # p1 is the list of all installed packages # p2 is the list of "leaf" packages # p3 is the list of packages recursively required by "leaf" packages echo "========================================================================" echo "(This may take a minute or so)" echo "# generated at $( date --rfc-3339=sec )" > /tmp/djh-check.p1.log cygcheck -c >> /tmp/djh-check.p1.log # our sed script does: # delete first 2 lines. # remove everything after the first space to end of line. # delete line(s) starting with '_'. sed < /tmp/djh-check.p1.log \ -e '1,2d' \ -e 's/ .*$//' \ -e '/^_/d' \ > /tmp/djh-check.p1a.log p1=( $( sort --unique /tmp/djh-check.p1a.log ) ) printf "${FMT}" "cgcheck -c" ${#p1[*]} ( echo "p1 =" ; echo " ${p1[*]}" ) > /tmp/djh-check.p1b.log echo "========================================================================" echo "(This may take a minute or so)" echo "# generated at $( date --rfc-3339=sec )" > /tmp/djh-check.dep-pp2.log ( cygcheck-dep -c -l -I 2> /dev/null || true ) >> /tmp/djh-check.dep-pp2.log # our sed script does: # delete lines starting with '/', \s+, ' _'. # remove open and closing parenthesis. # strip leading spaces. # strip trailing spaces. # replace multiple spaces with a single space. # delete empty lines. # replace spaces with newlines. sed < /tmp/djh-check.dep-pp2.log \ -e '/^\//d' \ -e '/^\#/d' \ -e '/^ _/d' \ -e '/^ ( /s/[()]//g' \ -e 's/\[Base\]//g' \ -e 's/^ *//' \ -e 's/ *$//' \ -e 's/ */ /' \ -e '/^$/d' \ -e 's/ /\n/g' \ > /tmp/djh-check.dep-pp2a.log p2=( $( sort --unique /tmp/djh-check.dep-pp2a.log ) ) printf "${FMT}" "cygcheck-dep -c -l -I" ${#p2[*]} ( echo "p2 =" ; echo " ${p2[*]}" ) > /tmp/djh-check.dep-pp2b.log echo "========================================================================" echo "(This may take a minute or so)" echo "# generated at $( date --rfc-3339=sec )" > /tmp/djh-check.dep-pp3.log ( cygcheck-dep -c -R ${p2[*]} 2> /dev/null || true ) >> /tmp/djh-check.dep-pp3.log # our sed script does: # delete lines starting with '_'. # remove 'recursively requires ' comments. # remove ')'. # strip leading spaces. # strip trailing spaces. # replace multiple spaces with a single space. # delete empty lines. # replace spaces with newlines. sed < /tmp/djh-check.dep-pp3.log \ -e '/^\#/d' \ -e '/^_/d' \ -e 's/: recursively requires ( */ /' \ -e 's/ *) *$//' \ -e 's/^ *//' \ -e 's/ *$//' \ -e '/^$/d' \ -e 's/ /\n/g' \ > /tmp/djh-check.dep-pp3a.log p3=( $( sort --unique /tmp/djh-check.dep-pp3a.log ) ) printf "${FMT}" "cygcheck-dep -c -R" ${#p3[*]} ( echo "p3 =" ; echo " ${p3[*]}" ) > /tmp/djh-check.dep-pp3b.log fi echo "========================================================================" # create the djh-reinstall command script. if [ -z $LONG ] ; then # use short options to save space opt_delete_orphans=-o opt_local_package_dir=-l opt_no_desktop=-d opt_no_shortcuts=-n opt_no_verify=-X opt_only_site=-O opt_packages=-P opt_pubkey=-K opt_quiet=-q opt_root=-R opt_site=-s opt_upgrade_also=-g else # use long options for readability opt_delete_orphans=--delete-orphans opt_local_package_dir=--local-package-dir opt_no_desktop=--no-desktop opt_no_shortcuts=--no-shortcuts opt_no_verify=--no-verify opt_only_site=--only-site opt_packages=--packages opt_pubkey=--pubkey opt_quiet=--quiet-mode opt_root=--root opt_site=--site opt_upgrade_also=--upgrade-also fi # echo "DEBUG: \$CMD_U = $CMD_U" rm -f ${CMD_U} touch ${CMD_U} cat >> ${CMD_U} <