#!/bin/bash
#
-# This script prepares a Windows binary distribution of wimlib on Linux using
-# MinGW-w64. The desired architecture must be passed as the first argument.
+# This script prepares a Windows binary distribution of wimlib.
-set -e
+set -e -u
-if [ ! -e src/wim.c ]; then
- echo "This script must be run from the toplevel directory" 1>&2
- exit 1
-fi
-
-if [ $# -lt 1 ]; then
- echo "Usage: $0 i686|x86_64 [EXTRA_CONFIGURE_ARG]..." 1>&2
- exit 1
-fi
+SCRIPTNAME="$0"
+TOPDIR=$(dirname "$(dirname "$(realpath "$0")")")
+cd "$TOPDIR" # Top-level directory of the git repo
-ARCH="$1"
-shift
+# Global variables, read-only after parse_options has run
+ARCH=
+DESTDIR=
+EXTRA_CONFIGURE_ARGS=
+INCLUDE_DOCS=false
+INSTALL_PREREQUISITES=false
+MAKE="make -j$(getconf _NPROCESSORS_ONLN)"
+MSYSTEM=${MSYSTEM:-}
+SKIP_CONFIGURE=false
+VERSION=$(tools/get-version-number)
+ZIP=false
+ZIPFILE=
-case "$ARCH" in
-i686|x86_64)
- ;;
-*)
- echo "ERROR: ARCH must be i686 or x86_64" 1>&2
- exit 1
- ;;
-esac
+PREBUILT_LLVM_MINGW_ENABLED=false
+PREBUILT_LLVM_MINGW_URL=https://github.com/mstorsjo/llvm-mingw/releases/download/20230320/llvm-mingw-20230320-msvcrt-x86_64.zip
+PREBUILT_LLVM_MINGW_ZIP=$(basename "$PREBUILT_LLVM_MINGW_URL")
+PREBUILT_LLVM_MINGW=${PREBUILT_LLVM_MINGW_ZIP%.zip}
+PREBUILT_LLVM_MINGW_BIN="/$PREBUILT_LLVM_MINGW/bin"
-VERSION=$(tools/get-version-number)
-DESTDIR=wimlib-${VERSION}-windows-${ARCH}-bin
-ZIPFILE=wimlib-${VERSION}-windows-${ARCH}-bin.zip
-MAKE="make -j $(grep -c processor /proc/cpuinfo)"
-
-# Compile wimlib
-
-if ! [ -e config.log ] ||
- ! grep -q "./configure --host=${ARCH}-w64-mingw32" config.log || \
- ! grep -q "configure: exit 0" config.log || \
- [ $# -gt 0 ]
-then
- # Note: putting -static-libgcc in CC is a workaround for libtool
- # stripping it:
- # http://www.gnu.org/software/libtool/manual/libtool.html#Stripped-link-flags
- ./configure --host=${ARCH}-w64-mingw32 --disable-static \
- CC="${ARCH}-w64-mingw32-gcc -static-libgcc" "$@"
- $MAKE clean
-fi
-$MAKE
+usage()
+{
+ cat << EOF
+Usage: $SCRIPTNAME [OPTION]... [EXTRA_CONFIGURE_ARG]...
+Options:
+ --arch=ARCH Specify the CPU architecture. This is unnecessary
+ when using MSYS2.
-# Create empty destination directory
+ --include-docs Build and install the PDF manual pages.
-rm -rf $DESTDIR
-mkdir $DESTDIR
+ --install-prerequisites Install the prerequisite packages needed to build
+ wimlib. This is only supported in MSYS2. You can
+ omit this if you have already done this for the same
+ MSYS2 environment. This option normally only
+ installs MSYS2 packages, but for ARM64 cross-builds
+ it also installs a separate prebuilt toolchain.
-# Install binaries
+ --skip-configure Skip running the configure script again. You can
+ use this to save time in incremental builds if you
+ are sure you didn't change any options.
-cp .libs/*.{dll,exe} $DESTDIR
-${ARCH}-w64-mingw32-strip $DESTDIR/*.{dll,exe}
+ --zip Zip the output files up into a zip file.
+EOF
+}
-# Install text files
+parse_options()
+{
+ if [ -z "$MSYSTEM" ]; then
+ ARCH=x86_64
+ else
+ case "$MSYSTEM" in
+ MINGW32)
+ ARCH=i686
+ CC_PKG=mingw-w64-i686-gcc
+ ;;
+ MINGW64)
+ ARCH=x86_64
+ CC_PKG=mingw-w64-x86_64-gcc
+ ;;
+ CLANG32)
+ ARCH=i686
+ CC_PKG=mingw-w64-clang-i686-clang
+ ;;
+ CLANG64)
+ ARCH=x86_64
+ CC_PKG=mingw-w64-clang-x86_64-clang
+ ;;
+ CLANGARM64)
+ ARCH=aarch64
+ # MSYS2 doesn't yet support cross-compiling for ARM64,
+ # so use a separate prebuilt toolchain for that case.
+ if [ "$(uname -m)" = x86_64 ]; then
+ PREBUILT_LLVM_MINGW_ENABLED=true
+ export PATH="$PREBUILT_LLVM_MINGW_BIN:$PATH"
+ else
+ CC_PKG=mingw-w64-clang-aarch64-clang
+ fi
+ ;;
+ *)
+ echo 1>&2 "Unsupported MSYS2 environment: $MSYSTEM. This script supports"
+ echo 1>&2 "MINGW32, MINGW64, CLANG32, CLANG64, and CLANGARM64."
+ echo 1>&2 "See https://www.msys2.org/docs/environments/"
+ exit 1
+ esac
+ fi
-cp NEWS README* COPYING* $DESTDIR
+ local longopts="help"
+ longopts+=",arch:"
+ longopts+=",include-docs"
+ longopts+=",install-prerequisites"
+ longopts+=",skip-configure"
+ longopts+=",zip"
-sed -n '/^#/q; s/^[\/\* ]*//; p' src/divsufsort.c > $DESTDIR/COPYING.libdivsufsort-lite
-if ! grep -q 'Copyright' $DESTDIR/COPYING.libdivsufsort-lite; then
- echo "ERROR: failed to extract libdivsufsort-lite license text" 1>&2
- exit 1
-fi
-(
- cd $DESTDIR
- for fil in NEWS README* COPYING*; do
- sed < $fil > ${fil}.txt -e 's/$/\r/g'
- rm $fil
+ local options
+ if ! options=$(getopt -o "" -l "$longopts" -- "$@"); then
+ usage 1>&2
+ exit 1
+ fi
+ eval set -- "$options"
+ while true; do
+ case "$1" in
+ --help)
+ usage
+ exit 0
+ ;;
+ --arch)
+ ARCH=$2
+ shift
+ ;;
+ --include-docs)
+ INCLUDE_DOCS=true
+ ;;
+ --install-prerequisites)
+ if [ -z "$MSYSTEM" ]; then
+ echo 1>&2 "--install-prerequisites is only supported in MSYS2."
+ exit 1
+ fi
+ INSTALL_PREREQUISITES=true
+ ;;
+ --skip-configure)
+ SKIP_CONFIGURE=true
+ ;;
+ --zip)
+ ZIP=true
+ ;;
+ --)
+ shift
+ break
+ ;;
+ *)
+ echo 1>&2 "Invalid option '$1'"
+ usage 1>&2
+ exit 1
+ ;;
+ esac
+ shift
done
-)
+ case "$ARCH" in
+ i686|x86_64|aarch64)
+ ;;
+ *)
+ echo 1>&2 "Unknown ARCH: $ARCH. Please specify a supported architecture with --arch"
+ exit 1
+ ;;
+ esac
+ DESTDIR=wimlib-${VERSION}-windows-${ARCH}-bin
+ ZIPFILE=$DESTDIR.zip
+ EXTRA_CONFIGURE_ARGS=("$@")
+}
+install_prebuilt_llvm_mingw()
+{
+ if [ -e "$PREBUILT_LLVM_MINGW_BIN" ]; then
+ echo "Prebuilt $PREBUILT_LLVM_MINGW is already installed"
+ return
+ fi
+ echo "Downloading $PREBUILT_LLVM_MINGW_ZIP..."
+ wget "$PREBUILT_LLVM_MINGW_URL" -O "/$PREBUILT_LLVM_MINGW_ZIP"
+ echo "Unzipping $PREBUILT_LLVM_MINGW_ZIP..."
+ unzip "/$PREBUILT_LLVM_MINGW_ZIP" -d /
+ if [ ! -e "$PREBUILT_LLVM_MINGW_BIN" ]; then
+ echo 1>&2 "$PREBUILT_LLVM_MINGW_BIN not found after unzip"
+ exit 1
+ fi
+ echo "Done installing prebuilt toolchain $PREBUILT_LLVM_MINGW"
+}
-# Install man pages
+install_prerequisites()
+{
+ echo "Installing the MSYS2 $MSYSTEM packages needed to build wimlib..."
+ local packages=(autoconf automake git libtool make pkgconf)
+ if "$PREBUILT_LLVM_MINGW_ENABLED"; then
+ echo "Will use prebuilt toolchain instead of MSYS2 one"
+ packages+=(wget unzip)
+ else
+ packages+=("$CC_PKG")
+ fi
+ pacman -Syu --noconfirm --needed "${packages[@]}"
+ echo "Done installing the MSYS2 $MSYSTEM packages needed to build wimlib."
-mkdir $DESTDIR/doc
+ if $PREBUILT_LLVM_MINGW_ENABLED; then
+ install_prebuilt_llvm_mingw
+ fi
+}
-function gen_pdf_from_man_page() {
- local manbase=$1
- local pdf=${DESTDIR}/doc/${manbase}.pdf
+bootstrap_repository()
+{
+ echo "Bootstrapping the wimlib repository..."
+ ./bootstrap
+}
- echo "Generating $pdf"
+configure_wimlib()
+{
+ echo "Configuring wimlib..."
+ local configure_args=("--host=${ARCH}-w64-mingw32")
+ configure_args+=("--disable-static")
+ # -static-libgcc is needed with gcc. It should go in the CFLAGS, but
+ # libtool strips it, so it must go directly in CC instead. See
+ # http://www.gnu.org/software/libtool/manual/libtool.html#Stripped-link-flags
+ local cc="${ARCH}-w64-mingw32-cc"
+ if ! type -P "$cc" &>/dev/null; then
+ cc="${ARCH}-w64-mingw32-gcc"
+ fi
+ if "$cc" --version | grep -q '(GCC)'; then
+ configure_args+=("CC=$cc -static-libgcc")
+ fi
+ configure_args+=("${EXTRA_CONFIGURE_ARGS[@]}")
+ ./configure "${configure_args[@]}"
+ $MAKE clean
+}
- MANPATH="./doc" man -t $manbase | ps2pdf - $pdf
+build_wimlib()
+{
+ echo "Building wimlib..."
+ $MAKE
}
-for fil in ./doc/man1/wim*.1; do
- manbase=`basename $fil`
- cmd=${manbase%.1}
- case $cmd in
- wimlib-imagex|wimmount|wimmountrw|wimunmount)
- continue
- ;;
- esac
+list_imagex_commands()
+{
+ for cmd in ./doc/man1/wim*.1; do
+ local cmd=${cmd##*/}
+ cmd=${cmd%.1}
+ case "$cmd" in
+ wimlib-imagex|wimmount|wimmountrw|wimunmount)
+ ;;
+ *)
+ echo "$cmd"
+ ;;
+ esac
+ done
+}
- gen_pdf_from_man_page $cmd
+install_binaries()
+{
+ echo "Installing binaries..."
+ cp .libs/*.{dll,exe} "$DESTDIR"
+ strip "$DESTDIR"/*.{dll,exe}
+}
- sed 's/$/\r/g' > ${DESTDIR}/${cmd}.cmd <<- EOF
- @echo off
- "%~dp0\\wimlib-imagex" ${cmd#wim} %*
- EOF
- chmod +x ${DESTDIR}/${cmd}.cmd
-done
+install_text_files()
+{
+ echo "Installing NEWS, README, and licenses..."
+ cp NEWS README* COPYING* "$DESTDIR"
+ sed -n '/^#/q; s/^[\/\* ]*//; p' src/divsufsort.c > "$DESTDIR"/COPYING.libdivsufsort-lite
+ if ! grep -q 'Copyright' "$DESTDIR"/COPYING.libdivsufsort-lite; then
+ echo 1>&2 "ERROR: failed to extract libdivsufsort-lite license text"
+ exit 1
+ fi
+ cd "$DESTDIR"
+ for fil in NEWS README* COPYING*; do
+ sed < "$fil" > "${fil}".txt -e 's/$/\r/g'
+ rm "$fil"
+ done
+ cd ..
+}
+
+gen_pdf_from_man_page()
+{
+ local cmd=$1
+ local pdf=${DESTDIR}/doc/${cmd}.pdf
+
+ echo "Generating $pdf"
+ MANPATH="./doc" man -t "$cmd" | ps2pdf - "$pdf"
+}
-gen_pdf_from_man_page wimlib-imagex
+install_pdf_docs()
+{
+ echo "Installing PDF manual pages..."
+ mkdir "$DESTDIR"/doc
+ for cmd in $(list_imagex_commands); do
+ gen_pdf_from_man_page "$cmd"
+ done
+ gen_pdf_from_man_page wimlib-imagex
+}
-# Install development files
+install_cmd_aliases()
+{
+ echo "Installing wim*.cmd files..."
+ for cmd in $(list_imagex_commands); do
+ sed 's/$/\r/g' > "${DESTDIR}/${cmd}.cmd" <<- EOF
+ @echo off
+ "%~dp0\\wimlib-imagex" ${cmd#wim} %*
+ EOF
+ chmod +x "${DESTDIR}/${cmd}.cmd"
+ done
+}
-mkdir $DESTDIR/devel
-cp .libs/libwim.dll.a $DESTDIR/devel/libwim.lib
-cp include/wimlib.h $DESTDIR/devel/
+install_development_files()
+{
+ echo "Installing development files..."
+ mkdir "$DESTDIR"/devel
+ cp .libs/libwim.dll.a "$DESTDIR"/devel/libwim.lib
+ cp include/wimlib.h "$DESTDIR"/devel/
+}
-# Generate ZIP file
+create_zip_file()
+{
+ echo "Creating zip file..."
+ cd "$DESTDIR"
+ 7z -mx9 a ../"$ZIPFILE" . > /dev/null
+ cd ..
+}
-rm -f $ZIPFILE
-(
- dir=$PWD
- cd $DESTDIR
- 7z -mx9 a "$dir/$ZIPFILE" .
-)
+parse_options "$@"
+rm -rf -- "$DESTDIR" "$ZIPFILE"
+mkdir -- "$DESTDIR"
+if $INSTALL_PREREQUISITES; then
+ install_prerequisites
+fi
+if [ ! -e configure ]; then
+ bootstrap_repository
+fi
+if [ ! -e config.log ] || ! $SKIP_CONFIGURE; then
+ configure_wimlib
+fi
+build_wimlib
+install_binaries
+install_text_files
+if $INCLUDE_DOCS; then
+ install_pdf_docs
+fi
+install_cmd_aliases
+install_development_files
+if $ZIP; then
+ create_zip_file
+ echo "Success! Output is in $ZIPFILE"
+else
+ echo "Success! Output is in $DESTDIR"
+fi