Building and packaging Wine 4.12.1 (32-bit and 64-bit) automatically

Posted

For a while now, I’ve been posting about compiling Wine on macOS, starting with Wine 1.9 in October 2016, then automating the compile for Wine 2.9 32-bit in May 2017. Now it’s July 2019, and this is probably my last post on the subject - a fully automated script to download and compile Wine 4.12.1 (released 7 July 2019) 32-bit and 64-bit on macOS.

Last post because:

  • There are pre-compiled official builds and supported (paid) versions
  • macOS Catalina 10.15 will kill support for 32-bit apps
  • I don’t use Wine at all

Disclaimer - don’t blindly trust scripts / code you download from the Internet, don’t run my code below as I don’t warrant it for any purpose, don’t use the version of Wine the script builds because it barely works even for me, and don’t even open Terminal unless you know what you’re doing!

But here are some things I did - maybe you can learn something new:

  • Download using curl
  • Compile code to a custom build directory
  • Set environment variables and parameters for configure and make for 32-bit and 64-bit
  • Relink binaries and library files with @loader_path relative to the package
  • Package all required files into an AppBundle which shows up as a single .app “file” - see my in-depth post
  • Tweak some Wine settings - retina support, and turn off Firefox hardware acceleration (which I never got working)
  • Display a dialog box from a shell script using osascript, and checking the choice
  • Converting a vector image (SVG) to an icon (ICNS) using qlmanage and sips - note, transparency is not supported (alternatively, investigate something like Gapplin)
  • Doing all this as your normal user (no sudo)

You’ll need macOS Sierra 12.4 (I use a virtual machine) with Apple Xcode installed. I don’t think there is any other dependency.... certainly no need for a package manager (Homebrew, MacPorts, or Fink), and no need for XQuartz / X11 (which is still required by the official build).

So, here is my code. I’ve left all my junk code intact but commented out - stuff I couldn’t compile, stuff I couldn’t get working, stuff I didn’t need - just so that I don’t loose my code.

#!/bin/bash
target="$PWD"
export PATH="$target/bin:$PATH"
export PKG_CONFIG_PATH="$target/lib/pkgconfig"
export CPPFLAGS="-I$target/include"

# Pause...
pause() { 
 read -p "Press Return to continue..." -n1 
 echo ""
}
## Download (unless file already exists), extract, configure and build
##  $1  target folder to run configure (as defined in the download file)
##  $2  download location
##  $3+ optional configure parameters (--prefix automatically set) 
download_build() {
 echo ============================
 echo = BUILDING $1
 echo ============================
 export CFLAGS="-arch x86_64 -m64 -arch i386 -m32 -O2 -w -I$target/include"
 export LDFLAGS="-arch x86_64 -arch i386 -L$target/lib"
 download_build_core ${@:1}
}
download_build32() {
 echo ============================
 echo = BUILDING 32-bit $1
 echo ============================
 export CFLAGS="-arch i386 -m32 -O2 -w -I$target/include"
 export LDFLAGS="-arch i386 -L$target/lib"
 download_build_core ${@:1}
}
download_build64() {
 echo ============================
 echo = BUILDING 64-bit $1
 echo ============================
 export CFLAGS="-arch x86_64 -m64 -O2 -w -I$target/include"
 export LDFLAGS="-arch x86_64 -L$target/lib"
 download_build_core ${@:1}
}
download_build_core() {
 file=$(basename "$2") 
 mkdir -p downloads
 [[ ! -f "downloads/$file" ]] && curl -L $2 -o "downloads/$file"
 [[ ! -d $1 ]] && tar xf "downloads/$file"
 pushd "$1" >/dev/null
 ./configure -q --prefix="$target" ${@:3}
 make -s uninstall >/dev/null 2>/dev/null
 make -s -j5 -k
 make -s -j5 install >/dev/null
 popd >/dev/null
}
download_build_openssl() {
 echo ============================
 echo = BUILDING 64-bit $1
 echo ============================
 export CFLAGS="-arch x86_64 -m64 -O2 -w -I$target/include"
 export LDFLAGS="-arch x86_64 -L$target/lib"
 file=$(basename "$2") 
 [[ ! -f $file ]] && curl -L $2 -o "$file"
 [[ ! -d $1 ]] && tar xf "$file"
 pushd "$1" >/dev/null
 ./configure --prefix="$target" darwin64-x86_64-cc enable-ec_nistp_64_gcc_128 ${@:3}
 #./configure --prefix="$target" darwin-i386-cc ${@:3}
 make -s uninstall >/dev/null 2>/dev/null
 make -s -j5 -k
 make -s -j5 install >/dev/null
 popd >/dev/null
}
# Similar to above, but hard coded for wine 64-bit build followed by 32-bit build (makefile takes care of which)
download_build_wine() {
 echo ============================
 echo = BUILDING $1
 echo ============================
 export CFLAGS="-O2 -w -I$target/include" 
 export LDFLAGS="-L$target/lib"
 file=$(basename "$2") 
 mkdir -p downloads
 [[ ! -f "downloads/$file" ]] && curl -L $2 -o "downloads/$file"
 [[ ! -d $1 ]] && tar xf "downloads/$file"
 pushd "$1" >/dev/null
 mkdir -p wine64 wine32
 cd wine64
 ../configure --prefix="$target" --without-x --enable-win64 ${@:3}
 make -s uninstall >/dev/null 2>/dev/null
 make -s -j5 install >/dev/null
 cd ../wine32
 ../configure --prefix="$target" --without-x --with-wine64=../wine64 ${@:3}
 make -s uninstall >/dev/null 2>/dev/null
 make -s -j5 install >/dev/null
 popd >/dev/null
}
## Relink dynamic library files, slightly different to replace id's
##  $1  folder containing library files
##  $2  target library folder
relink_library() {
 echo ============================
 echo = RELINKING $1
 echo ============================
 pushd "$1" >/dev/null
 for file in *; do
  if [[ ! -L "$file" && "$file" != *".old" && $(file "$file") == *"Mach-O"* ]]; then
   echo Relink $file
   while read -r full; do 
    dir=$(dirname "$full")
    name=$(basename "$full") 
    [[ "$full" == *"/$file" ]] && install_name_tool -id "$name" "$file"
    [[ "$full" != *"/$file" && "$full" == "$target/lib/$name" ]] && install_name_tool -change "$full" "$2/$name" "$file"
    [[ "$full" == "bin/$name" ]] && install_name_tool -change "$full" "$2/$name" "$file"
   done < <(otool -L "$file" | tail -n +2 | cut -d'(' -f1)
   fi
 done
 popd >/dev/null
}
## Update last section of default wine.inf (registry) for retina mode
##  Lazy, assumptions. 3rd param typically 0 (string) or 0x00010001 (dword)
##  $1  location of wine.inf
tweak_wineinf_retina() {
 mkdir -p "$1"
 echo 'HKCU,Software\Wine\Mac Driver,"RetinaMode",,"y"
HKCU,Control Panel\Desktop,"FontSmoothing",,"2"
HKCU,Control Panel\Desktop,"FontSmoothingOrientation",0x00010001,0x00000001
HKCU,Control Panel\Desktop,"FontSmoothingType",0x00010001,0x00000002
HKCU,Control Panel\Desktop,"FontSmoothingGamma",0x00010001,0x00000578
HKLM,System\CurrentControlSet\Hardware Profiles\Current\Software\Fonts,"LogPixels",0x00010001,0x000000cc
' >> "$1/wine.inf"
}
## Fix Firefox which has a process error if this is not done
##  From https://bugs.winehq.org/show_bug.cgi?id=42388
##  $1  optional wine prefix folder (default ~/.wine)
tweak_mozila_prefs() {
  [[ -d "$1" ]] && cd "$1" || cd ~/.wine
 for f in $(ls -d drive_c/users/*/Application\ Data/Mozilla/Firefox/Profiles/*/prefs.js); do
 echo 'user_pref("browser.tabs.remote.autostart",false);' >> "$f"
#  echo 'user_pref("browser.tabs.remote.autostart2",false);' >> "$f"
#  echo 'user_pref("browser.tabs.remote.autostart.2",false);' >> "$f"
#  echo 'user_pref("security.sandbox.content.level",0);' >> "$f"
 done
}
## Create wine.app with 2 mandatory files, 2 custom files and other Resources
##  $1  name and location of wine.app
create_app() {
 echo ============================
 echo = CREATING $1
 echo ============================
 mkdir -p "$1/Contents/MacOS" "$1/Contents/Resources"
 pushd "$1/Contents" >/dev/null
 echo "APPL????" > PkgInfo
 echo '<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0"><dict>
<key>CFBundleExecutable</key><string>startwine</string>
<key>CFBundleIconFile</key><string>wine.icns</string>
<key>CFBundleIdentifier</key><string>com.myByways.wine</string>
<key>CFBundlePackageType</key><string>APPL</string>
<key>CFBundleVersion</key><string>1.9.21</string>
<key>CFBundleSupportedPlatforms</key><array><string>MacOSX</string></array>
<key>LSApplicationCategoryType</key><string>public.app-category.utilities</string>
<key>LSMinimumSystemVersion</key><string>10.6</string>
</dict></plist>' > Info.plist
 echo '#!/bin/bash
path=$(dirname $0)
[[ "$path" == *"/"* ]] && cd "$path"
[[ "$PWD" == *"/MacOS" ]] && cd "../Resources/bin"
[[ "$PATH" != *"$PWD"* ]] && export PATH="$PWD":$PATH
unset WINEPREFIX
prefixes=""
while IFS= read -r line; do
 prefixes=$prefixes",\"$(dirname "$line")\""
done < <( ls -d ~/.*/drive_c )
if [[ prefixes != "" ]]; then
 prefixes="\"[New]\"$prefixes"
 choice=$(osascript -e "return choose from list { $prefixes } with prompt \"Select Wine folder\" with title \"Start Wine\" default items {\"~/.Wine\"}")
 [[ $choice == "false" ]] && exit 0
 [[ $choice == "[New]" ]] && export WINEPREFIX=`date +$HOME/.wine%Y%m%d%H%M%S` || export WINEPREFIX=$choice
fi
exec winefile C:\\' > "MacOS/startwine"
 chmod +x "MacOS/startwine"
 cd Resources
 cp -R "$target/bin" .
 cp -R "$target/lib" .
 cp -R "$target/lib64" .
 mkdir -p share
 cp -R "$target/share/wine" share
 rmdir lib/gpext lib/nss_info lib/pdb lib/perfcount 
 if [[ ! -f wine.icns ]]; then
  curl -L https://dl.winehq.org/wine/logos/wine_logo.svg -o wine_logo.svg
  qlmanage -t -s 512 -o . wine_logo.svg
  sips -s format icns wine_logo.svg.png --out wine.icns
  rm wine_logo.svg wine_logo.svg.png
 fi
 popd >/dev/null
}

# Wine 4.12 latest must be compiled with bison 3+ which is not incldued on macOS Mojave 10.14
download_build bison-3.3 https://ftp.gnu.org/gnu/bison/bison-3.3.tar.xz

## Mandatory graphics libraries
## Sources: http://libpng.org http://freetype.org http://ijg.org
download_build libpng-1.6.37 http://downloads.sourceforge.net/project/libpng/libpng16/1.6.37/libpng-1.6.37.tar.gz --disable-static
download_build jpeg-9c http://ijg.org/files/jpegsrc.v9c.tar.gz --disable-static
download_build freetype-2.10.1 http://download.savannah.gnu.org/releases/freetype/freetype-2.10.1.tar.gz --disable-static
cp "$target/include/freetype2/ft2build.h" "$target/include/"
cp -R "$target/include/freetype2/freetype" "$target/include/"

## Optional graphics libraries - circular dependency tiff + libwebp
## Sources: https://github.com/libusb/libusb/releases https://www.libsdl.org https://libgd.github.io/
download_build tiff-4.0.10 http://download.osgeo.org/libtiff/tiff-4.0.10.tar.gz --disable-static --without-x --with-jpeg-lib-dir="$target/lib" --with-jpeg-include-dir="$target/include"
download_build SDL2-2.0.10 https://www.libsdl.org/release/SDL2-2.0.10.tar.gz
download_build libwebp-1.0.3 https://storage.googleapis.com/downloads.webmproject.org/releases/webp/libwebp-1.0.3.tar.gz --disable-static --disable-gl --enable-libwebpmux --enable-libwebpdemux --enable-libwebpdecoder
download_build libgd-2.2.5 https://github.com/libgd/libgd/releases/download/gd-2.2.5/libgd-2.2.5.tar.xz --disable-static --without-x --with-png="$target" --with-freetype="$target" --with-tiff="$target"
download_build lcms2-2.9 http://freefr.dl.sourceforge.net/project/lcms/lcms/2.9/lcms2-2.9.tar.gz --disable-static --with-jpeg="$target" --with-tiff="$target"
download_build mpg123-1.25.11 https://downloads.sourceforge.net/project/mpg123/mpg123/1.25.11/mpg123-1.25.10.tar.bz2 --disable-static --disable-debug --with-default-audio=coreaudio --with-cpu=generic

# Other graphics libraries
# Failed: gphoto, which requires libtool and pkg-config (64-bit only, error if compiling BOTH 64-bit + 32-bit) but still does not work because gcc line needs -lgphoto2_port parameter
#download_build libtool-2.4.6 https://ftp.gnu.org/gnu/libtool/libtool-2.4.6.tar.xz --disable-static --program-prefix=g --enable-ltdl-install
#export CFLAGS="-arch x86_64 -m64 -O2 -I$target/include"
#export LDFLAGS="-arch x86_64 -L$target/lib"
#download_build pkg-config-0.29.2 https://pkg-config.freedesktop.org/releases/pkg-config-0.29.2.tar.gz --disable-static --with-internal-glib
#export CFLAGS="-arch x86_64 -arch i386 -m32 -m64 -O2 -I$target/include"
#export LDFLAGS="-arch x86_64 -arch i386 -L$target/lib"
#download_build libgphoto2-2.5.20 https://downloads.sourceforge.net/project/gphoto/libgphoto/2.5.20/libgphoto2-2.5.20.tar.bz2 --disable-static

# Failed: MoltenVK? No clue
# Source: https://github.com/KhronosGroup/MoltenVK pre-built binary https://vulkan.lunarg.com/sdk/home
#download_build MoltenVK-1.0.32 https://github.com/KhronosGroup/MoltenVK/archive/v1.0.32.tar.gz 

## Optional libraries
## Sources: http://libtiff.org http://www.littlecms.com http://www.unixodbc.org
download_build libusb-1.0.22 https://github.com/libusb/libusb/releases/download/v1.0.22/libusb-1.0.22.tar.bz2 --disable-static
download_build unixODBC-2.3.7 http://www.unixodbc.org/unixODBC-2.3.7.tar.gz --disable-static

## Mandatory samba 3 (ntlm for Word authentication) - repeated twice because first time will always fail (make, then make install)
download_build64 samba-3.6.25/source3 https://ftp.samba.org/pub/samba/samba-3.6.25.tar.gz  --sbindir="$target/bin" --disable-largefile --disable-swat --disable-smbtorture4 --disable-cups --disable-pie --disable-relro --disable-external-libtalloc --disable-external-libtdb --disable-fam --disable-dnssd --disable-avahi --without-dmapi --without-ldap --without-dnsupdate --without-pam --without-pam_smbpass --without-utmp --without-cluster-support --without-acl-support --without-sendfile-support --with-included-popt --with-included-iniparser --with-winbind

# Failed: OpenSSL for Kerberos - very yuck
# Source: https://www.libsdl.org https://web.mit.edu/kerberos/
#download_build_openssl openssl-1.0.2q https://www.openssl.org/source/openssl-1.0.2q.tar.gz no-ssl2 no-ssl3 no-zlib shared enable-cms
#download_build krb5-1.17/src https://web.mit.edu/kerberos/dist/krb5/1.17/krb5-1.17.tar.gz 

## Failed: Samba 4 does not compile
#download_build openldap-2.4.47 ftp://ftp.openldap.org/pub/OpenLDAP/openldap-release/openldap-2.4.47.tgz --disable-static --disable-bdb -disable-hdb --disable-mdb
#download_build samba-4.9.4 https://ftp.samba.org/pub/samba/samba-4.9.4.tar.gz --sbindir="$target/bin" --disable-cups --disable-avahi --without-dnsupdate --without-pam --without-utmp --without-cluster-support --without-acl-support --without-sendfile-support --with-winbind --without-json-audit --without-ad-dc --without-libarchive

ver=4.12.1
download_build_wine wine-$ver http://dl.winehq.org/wine/source/4.x/wine-$ver.tar.xz --with-freetype --with-netapi

## Create app and relink libraries
app="$target/wine$ver.app"
create_app "$app"
relink_library "$app/Contents/Resources/lib" "@loader_path"
relink_library "$app/Contents/Resources/bin" "@loader_path/../lib"
install_name_tool -add_rpath "@loader_path/../lib" "$app/Contents/Resources/lib64/libwine.dylib"

## Tweak to enable retina mode properly
tweak_wineinf_retina "$app/Contents/Resources/share/wine"

Again, all this is for me, not for you. Please don’t ask me anything :)

Aside: Wow, this is post #101... since 2015 - I really don’t know why I keep this site up.