Contents
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
- Contents
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 towin32
orwin64
LC_ALL
set to locale you need, for example, en_EN.UTF-8 or ru_RU.UTF-8 etcAPP
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 neednames
– array of application names, count must match with count ofvalues
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 and with winetricks fixes 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
I read your whole article and then arrived at “Automation”. Maybe a good idea to mention it at the start of the article as well 🙂
Hi. Artem, thank you for awesome post! Maybe you know which options I should use to prevent Mac from heating when I run JA2?) I make wine app, start it with “Open in low resolution” flag but it does not help. Also I have change monitor resolution to lowest but it does not help too. How to say Mac do not use all GPU power for this app?) Thanks!
Unfortunately, I don’t know. I guess you have mac with integrated gpu into cpu so any high cpu load (even without wine) will trigger heating and fans.
Hi Artem, thank you for the post. I have created a Wine application bundle to host the notepad app that comes with Wine as an example. It works great with one exception, when I click the app icon and then select file->open, the file open dialog appears but no files are listed in the directory. The directories and subdirectories appear, but no files are present (this includes any files I just saved via Wine app bundle). Strangely, if I manually type in the name of the file into the dialog, the file will open.
One other piece of information, if I manually run the launcher script via a shell (the Terminal app), the files appear in the dialog.
Do you have any thoughts or ideas?
I suspect the Launch Services is not granting full permissions and therefore I am missing a setting in the info.plist.
It could be sandboxing. Try add into plist:
Unfortunately that didn’t change behavior. Any other ideas?
One other thing I forgot mention is I am running on Catalina. But this may be irrelevant.
It could be but unfortunately I don’t have other ideas to try.
Thanx for manual!
What about wine-prefix permissions (owner) problem? For example I’ve created bundled App and put it in Applications folder (Mac), then another user on my computer gets “wine-prefix … is not owned by you” while launching it
I never tested in
/Applications
but tested in~/Applications
. Wine prefix need to be writeable and it is against Apple guidelines for/Applications
directory. I guess I need to explicitly state that apps produced by bundler need to be placed in/Users/$USER/Applications
.Thanks a lot! Worked great on the first try (and that’s a sentence I’ve never written before).