Capturing a Cygwin instance from another PC
Doug Henderson
djndnbvg@gmail.com
Thu Nov 23 20:27:52 GMT 2023
> Jose Isaias Cabrera via Cygwin writes:
> > I have a new Win11 PC, and I wanted to capture the same Cygwin setup
> > that I have in another Win10 PC. I copied the C:\cygwin64 folder from
> > the Win10 pc to the Win11 pc,
I use the attached script (rename it to remove the .txt extension) to
clone my cygwin installation.
You can use the generated script to
1. perform a 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.
USE AT YOUR OWN RISK.
You must change the variables in the configuration section to your requirements.
You can also edit the generated script, which is created in the same
folder as the cygwin setup executable.
HTH
Doug
--
Doug Henderson, Calgary, Alberta, Canada - from gmail.com
-------------- next part --------------
#!/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} <<EOF
@echo off
@rem This file is $CMD_W
@rem It was generated
@rem by ${USER}@${HOSTNAME}
@rem at $( date --rfc-3339=sec )
@rem using $( cygpath -au $0 )
@rem AKA $( cygpath -aw $0 )
@rem
@rem WARNING: Changes will be overwritten without warning.
@rem
@SETLOCAL
@SETLOCAL ENABLEEXTENSIONS
${SETUP_PATH_W} ^
$opt_quiet $opt_no_desktop $opt_no_shortcuts ^
$opt_delete_orphans $opt_upgrade_also $opt_only_site ^
$opt_root "${ROOT_W}" ^
$opt_local_package_dir "${CACHE_W}" ^
$( if [ -n "$OVERLAY" ]; then echo " $opt_no_verify $opt_site $OVERLAY ^\n" ; fi; )\
$opt_site ${MIRROR} ^
$opt_packages ${p2[*]}
$( if [ -n "$PAUSE" ]; then echo "@pause" ; else echo "@rem" ; fi )
EOF
# sed -i -e 's/ / /g' ${CMD_U}
echo "INFO: cmd line length (max=~8192): $( wc --max-line-length < ${CMD_U} )"
echo "INFO: when the line length exceeds the max, you must edit \"${CMD_W}\""
echo ""
# display the command script without non-echoed lines (optional)
# sed < ${CMD_U} -e '/^\@/d'
# Convert the command script from unix to dos line endings
unix2dos -ascii --keepdate --quiet --oldfile ${CMD_U}
# remove scatch files
if [ -z "$DEBUG" ] ; then
rm -f /tmp/djh-check.*
fi
# -30-
More information about the Cygwin
mailing list