Building a Universal MacOS Application

D.I.Y. universal application for x86_64 and arm64

The following steps are used to generate a universal app by command-line given two apps: RawTherapee-x86_64.app and RawTherapee-arm64.app. There is one conflict to resolve: the libgcc dependencies have different versions on each architecture. This is resolved by renaming the dependencies identically, and changing the installed names for those libs in the respective fftw3f and fftw3f_omp libs.

  1. Add this key with array to Info.plist to prefer native execution on arm64
<key>LSArchitecturePriority</key>
<array>
    <string>arm64</string>
    <string>x86_64</string>
</array>
  1. Run the following commands in the shell:
# Make a copy of the arm64 app
cp -R RawTherapee-arm64.app RawTherapee.app

# Deal with a library which has different versions on each arch.
# Goal: each lib has same filename and installed name for both archs.
install_name_tool -change RawTherapee.app/Contents/Frameworks/libgcc_s.1.dylib RawTherapee.app/Contents/Frameworks/libgcc.dylib RawTherapee-x86_64.app/Contents/Frameworks/libfftw3f.3.dylib
install_name_tool -change RawTherapee.app/Contents/Frameworks/libgcc_s.1.dylib RawTherapee.app/Contents/Frameworks/libgcc.dylib RawTherapee-x86_64.app/Contents/Frameworks/libfftw3f_omp.3.dylib
install_name_tool -change RawTherapee.app/Contents/Frameworks/libgcc_s.1.1.dylib RawTherapee.app/Contents/Frameworks/libgcc.dylib RawTherapee-arm64.app/Contents/Frameworks/libfftw3f_omp.3.dylib
install_name_tool -change RawTherapee.app/Contents/Frameworks/libgcc_s.1.1.dylib RawTherapee.app/Contents/Frameworks/libgcc.dylib RawTherapee-arm64.app/Contents/Frameworks/libfftw3f.3.dylib
mv RawTherapee-x86_64.app/Contents/Frameworks/libgcc_s.1.dylib RawTherapee-x86_64.app/Contents/Frameworks/libgcc.dylib
mv RawTherapee-arm64.app/Contents/Frameworks/libgcc_s.1.1.dylib RawTherapee-arm64.app/Contents/Frameworks/libgcc.dylib

# Create the fat main rawtherapee binary and move it into the new bundle
lipo -create -output rawtherapee RawTherapee-arm64.app/Contents/MacOS/rawtherapee RawTherapee-x86_64.app/Contents/MacOS/rawtherapee
mv rawtherapee RawTherapee.app/Contents/MacOS

# Create all the fat dependencies and move them into the bundle
for lib in RawTherapee-arm64.app/Contents/Frameworks/* ; lipo -create -output $(basename $lib) RawTherapee-arm64.app/Contents/Frameworks/$(basename $lib) RawTherapee-x86_64.app/Contents/Frameworks/$(basename $lib)
sudo mv *cli *so *dylib RawTherapee.app/Contents/Frameworks

# Sign app and compress for notarization
codesign --force --deep --all-architectures --timestamp --strict -v -s "Developer ID Application: Doctor Who (1234567890)" -i com.rawtherapee.RawTherapee -o runtime --entitlements ~/repo-rt/tools/osx/rt.entitlements RawTherapee.app
ditto -c -k --sequesterRsrc --keepParent RawTherapee.app RawTherapee.app.zip

# Notarize app, check notary status, staple ticket
xcrun altool --notarize-app --primary-bundle-id "com.rawtherapee.RawTherapee" --username drwho@bbc.com --password abcd-efgh-ijkl-mnop --file RawTherapee.app.zip
xcrun altool --notarization-info fe822b83-af86-4136-99ab-ce089c8fdeaa --username drwho@bbc.com --password abcd-efgh-ijkl-mnop
xcrun stapler staple RawTherapee.app

𝒱ℴ𝒾𝓁𝒢, a universal app that may be distributed to users of both Intel and Apple Silicon Macs.


More information about generating Universal macOS apps:

Building a Universal macOS Binary | Apple Developer Documentation

1 Like