How to wrap WINE applications into macOS application bundle

Published on Author Artem ButusovLeave a comment

Why?

Bundling your application with wine and all application-specific settings into dedicated sandbox could be a very good idea. It will also give you an easy way to double click and run and there will be a nice icon to distinguish wine app from other application

Plan

You could create application bundle manually from command line. You will need:

  • specific directory structure
  • simple application plist file
  • simple application launcher
  • preconfigured wine prefix
  • portable wine
  • (optional) application chooser
  • (optional) application icon

Application bundle structure

“My App” is an example application name, could be anything.

This proposed directory structure is not the only possible way, but it could be a good starting point for your customer bundle.

Please note, we are assuming that application will write to it is own directory in wine prefix, so application bundle should be install in user space “Applications” directory. You could manually create directory “Applications” in your home directory and create wine bundle in this directory.

  • My App.app
    • Contents
      • Info.plist
      • MacOS
        • My App
      • Resources
        • My App.icns
        • wine-home
        • wine-prefix
        • launcher.scpt

Create directory structure

Create a directory for all user-space applications:

mkdir -p ~/Applications

Create an application directory structure:

cd ~/Applications
mkdir -p "My App.app/Contents/"{MacOS,Resources}

Create plist

Edit application plist: nano "My App.app/Contents/Info.plist"

<?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>My App</string>
    <key>CFBundleGetInfoString</key>
    <string>My App</string>
    <key>CFBundleIconFile</key>
    <string>My App</string>
    <key>CFBundleName</key>
    <string>My App</string>
    <key>CFBundlePackageType</key>
    <string>APPL</string>
    <key>CFBundleSignature</key>
    <string>4242</string>
    <key>NSHighResolutionCapable</key>
    <true/>
  </dict>
</plist>

NSHighResolutionCapable property is optional, will make chooser script look nicer on retina screens.

Replace My App with your application bundle name.

Create launcher script

Edit launcher: nano "My App.app/Contents/MacOS/My App"

#!/bin/sh

ROOT="$(cd "$(dirname "$0")" && pwd)"

RESOURCES="$ROOT/../Resources"

export WINEPREFIX="$RESOURCES/wine-prefix"
export WINEARCH="win32"
export LC_ALL="ru_RU.UTF-8"

APP=''
LAUNCHER="$RESOURCES/launcher.scpt"
WINE="$RESOURCES/wine-home/usr/bin/wine"

if [ -f "$LAUNCHER" ]; then
  APP="$(osascript "$LAUNCHER")"
fi

if [ -n "$APP" ]; then
  if [ "${APP/\\}" = "$APP" ]; then
    "$WINE" "$APP"
  else
    WINEPATH="$RESOURCES/wine-home/usr/bin/winepath"
    APP="$("$WINEPATH" "$APP")"
    cd "$(dirname "$APP")"
    "$WINE" "$(basename "$APP")"
  fi
fi

Don’t forget to mark launcher as executable:

chmod +x "My App.app/Contents/MacOS/My App"

Variables:

  • WINEARCH set to win32 or win64
  • LC_ALL set to locale you need, for example, en_EN.UTF-8 or ru_RU.UTF-8 etc
  • APP set to main application file in windows notation, like 'C:\Application Folder\application.exe'. You could leave it blank if you would like to use launcher.

Create chooser script (optional)

If you just need to launch one application within bundle then just set it in launcher and skip this file.

Edit chooser: nano "My App.app/Contents/Resources/launcher.scpt"

on getPositionOfItemInList(theItem, theList)
  repeat with a from 1 to count of theList
      if item a of theList is theItem then return a
  end repeat
  return 0
end getPositionOfItemInList

set title to "Select application"
set names to {"Application Name","Application Settings"}
set values to {"c:\\Application Folder\\application.exe","c:\\Application Folder\\settings.exe"}

set defaultName to item 1 of names
set selectedName to choose from list names with prompt title default items defaultName

set selectedValue to ""
if selectedName is not false then
  set selectedValue to item (getPositionOfItemInList((selectedName as string), names)) of values
end if

selectedValue

You will need to edit these variables:

  • title – chooser title, change if you need
  • names – array of application names, count must match with count of values
  • values – application paths in windows notation, replace all \ with \\

As mention before, this script is needed if you would like to prompt for application to launch. If it is just one application then you don’t need a prompt and you could set application path in launcher script.

Install wine binaries

Portable Wine versions could be downloaded here: https://dl.winehq.org/wine-builds/macosx/pool/

Just download any portable-winehq-*.tar.gz and unpack it to My App.app/Contents/Resources/wine-home

Final wine-home folder should have just one subfolder usr.

Install wine prefix

You must have already preconfigured prefix with installed application, tweaked registry or with winetricks fixed applied.

You just need to copy whole prefix into application bundle.

Example:

rsync -a ~/.wine/ ~/"Applications/My App.app/Contents/Resources/wine-prefix" --exclude "dosdevices"

Please note, there is a / in the end of ~/.wine/. It is required to properly copy directory tree.

Create an icon (optional)

Use existing icns icon

You could copy any icns icon to ~/Applications/My App.app/Contents/Resources/My App.icns.

For example, default application icon:

cp -fv /System/Library/CoreServices/CoreTypes.bundle/Contents/Resources/GenericApplicationIcon.icns ~/"Applications/My App.app/Contents/Resources/My App.icns"

This won’t work for non-icns icons, you will need to convert them to icns before.

Convert existing png icon to icns icon

For example, you have image.png in current working directory.

This image should be a high resolution icon, for example, 1024×1024 is the best.

# create temporary iconset
mkdir -p "image.iconset"

# create icons inside iconset based on source image
sips -z 16 16     "image.png" --out "image.iconset/icon_16x16.png"
sips -z 32 32     "image.png" --out "image.iconset/icon_16x16@2x.png"
sips -z 32 32     "image.png" --out "image.iconset/icon_32x32.png"
sips -z 64 64     "image.png" --out "image.iconset/icon_32x32@2x.png"
sips -z 128 128   "image.png" --out "image.iconset/icon_128x128.png"
sips -z 256 256   "image.png" --out "image.iconset/icon_128x128@2x.png"
sips -z 256 256   "image.png" --out "image.iconset/icon_256x256.png"
sips -z 512 512   "image.png" --out "image.iconset/icon_256x256@2x.png"
sips -z 512 512   "image.png" --out "image.iconset/icon_512x512.png"
sips -z 1024 1024 "image.png" --out "image.iconset/icon_512x512@2x.png"

# bundle iconset into icns file
iconutil -c icns "image.iconset"

# remove not needed anymore iconset
rm -rf "image.iconset"

There should be file image.icns in current working directory once you will finish.

Convert GOG’s icon to png file

GOG games have in most cases *.ico file for each game located in game installation folder.

You could convert it to png and then to icns and use it for application bundle as an icon.

For example, GOG’s “Divine Divinity” has C:\GOG Games\Divine Divinity\gfw_high.ico icon.

You will need homebrew installed. See here: https://brew.sh

And you will need to install ImageMagic thru homebrew: brew install imagemagick

For example, your icon gfw_high.ico is in current working directory.

Dump all frames inside icon:

identify gfw_high.ico | nl -v0

You will see something like below:

     0  gfw_high.ico[0] ICO 32x32 32x32+0+0 4-bit sRGB 121827B 0.010u 0:00.019
     1  gfw_high.ico[1] ICO 16x16 16x16+0+0 4-bit sRGB 121827B 0.010u 0:00.019
     2  gfw_high.ico[2] ICO 48x48 48x48+0+0 8-bit sRGB 121827B 0.010u 0:00.019
     3  gfw_high.ico[3] ICO 32x32 32x32+0+0 8-bit sRGB 121827B 0.010u 0:00.019
     4  gfw_high.ico[4] ICO 16x16 16x16+0+0 8-bit sRGB 121827B 0.010u 0:00.019
     5  gfw_high.ico[1] PNG 256x256 256x256+0+0 8-bit sRGB 121827B 0.000u 0:00.009
     6  gfw_high.ico[1] ICO 48x48 48x48+0+0 8-bit sRGB 121827B 0.000u 0:00.000
     7  gfw_high.ico[2] ICO 32x32 32x32+0+0 8-bit sRGB 121827B 0.000u 0:00.000
     8  gfw_high.ico[3] ICO 16x16 16x16+0+0 8-bit sRGB 121827B 0.000u 0:00.000

Remember index that is associated with the frame with the best quality. In my case it is 5 index (PNG 256×256).

Extract frame from ico file into png:

convert 'gfw_high.ico[5]' gfw_high.png

Ok, now you will have gfw_high.png that could be manually converted to icns.

Touch bundle

Final action to flush any macOS caches (like icon, application metadata) is to touch application bundle.

touch ~/"Applications/My App.app"

Automation

I created a simple bash application named “wine bundler” that will do everything mentioned in this article in automated way.

You could read more about here: https://github.com/sormy/wine-bundler

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.