WILT: Universal Binaries and DMG for OSX
I've been building an app I call Helm
that combines a bunch of regular activities into one place. Alongsized a daily Zettlekasten status prompt it links together all the damn places that work assigns me jobs. The incident system, the planner system, the backlog system, and more. This could be useful for more than just me, so I wanted to have it compile to multiple OS-es.
Windows
Easy
1build-windows:
2 set GOOS=windows&&\
3 set GOARCH=amd64&&\
4 set CGO_ENABLED=1&&\
5 set CC="x86_64-w64-mingw32-gcc"&&\
6 cd src&&\
7 go build -ldflags "-w -s -H=windowsgui" -o ../bin/helm.exe -mod=readonly
Set the target architecture, enable CGO and a mingw32 compiler for building a windows binary on a mac, and then the build.
OS X
Intel and M1 versions
With OS X there's a wrinkle - the new M1 Macs have a different compile image to the Intel Macs. First off, I had to build two versions
1build-oldosx:
2 cd src && \
3 GOOS=darwin GOARCH=amd64 CGO_ENABLED=1 CGO_CFLAGS="-mmacosx-version-min=10.12" CGO_LDFLAGS="-mmacosx-version-min=10.12" go build -mod=readonly -o ../bin/macold
4
5build-newosx:
6 cd src && \
7 GOOS=darwin GOARCH=arm64 CGO_ENABLED=1 CGO_CFLAGS="-mmacosx-version-min=10.14" CGO_LDFLAGS="-mmacosx-version-min=10.14" go build -mod=readonly -o ../bin/macnew
Just like windows - set the target architecture and C compile flags and build. For OS X there's two target architectures, amd64
for Intel and arm64
for the new M1s. As I'm building a Fyne app in Golang, fyne comes with a nice .app
maker
1build-osx:
2 cd src && \
3 fyne package -os darwin && \
4 defaults write Helm.app/Contents/Info LSUIElement 1
Universal Binary
Now I have a Helm.app
that by default is built for M1. For this to work I need to replace the executable in there with a Universal binary. The Universal binary is built with the following:
1build-osxu: build-oldosx build-newosx build-osx
2 cd bin && \
3 lipo -create -output helm macold macnew && \
4 cp helm ../src/Helm.app/Contents/MacOS && \
5 codesign -f -s - ../src/Helm.app
With this command it requires the above Intel and M1 versions are built and there's a Helm.app
. lipo
is a command that creates universal binaries! I run that and then copy it to the right subfolder in Helm.app
and then I sign it.
DMG
To send this to other people, it's nice to have an installer as a .dmg
file. Looking online there's many ways to do it in a GUI, but here's the way I found to create the .dmg via the command line (thus part of my Makefile).
1installer: build-osxu
2 [[ -f Helm-Installer.dmg ]] && rm Helm-Installer.dmg
3 cp -r src/Helm.app src/dmgsource
4 create-dmg \
5 --volname "Helm Installer" \
6 --background "../assets/big-seal-arkark.png" \
7 --window-pos 200 120 \
8 --window-size 800 400 \
9 --icon-size 100 \
10 --icon "dmgsource/Helm.app" 200 190 \
11 --hide-extension "Helm.app" \
12 --app-drop-link 600 185 \
13 "Helm-Installer.dmg" \
14 "dmgsource/"
This command requiers the universal binary to be all in place in the Helm.app
directory. This script doesn't delete any existing .dmg files, so the first thing the command does is remove the Helm-Installer.dmg
if it exists. It then puts the Helm.app in an empty directory - this directory has everything that will be in the .dmg - at the moment all I want is that Helm.app
file.
create-dmg
is a script I got from Brew, I send to that all the required command flags to build the Helm-Installer.dmg
. When you run this tool it pops up a folder view prompting the user to copy the file into the /Applications/
folder on their Mac. Installed!