MacOS 10.15 expects command line tools should be notarized. All native code for ARM-based Apple Silicon
CPUs must be code signed. Apple provides some documentation, but most of this assumes projects managed using Xcode (example here). This creates a barrier for cross-platform projects that do not rely on XCode. Stéphane Sudre provides an outstanding graphcial tool named Packages that can aid this process ( example here). In contrast, this repository provides a minimum shell script for installing exectuables.
Assuming you have a working Terminal application you will need to complete the following steps:
- You will need an Apple developer account. With the account you will need to generate
Developer ID Installer
andDeveloper ID Application
certificates and install them on your computer.
- You can check which certificates are installed on your computer by running the terminal command
security find-identity -p basic -v
. This should listDeveloper ID Installer: My Name
andDeveloper ID Application: My Name
. You will only do this once.
- Generate an app specific password via https://appleid.apple.com. You will only do this once.
- Run the notarize.bash script. The first time you do this want to edit the Info.plist with your personal CFBundleName, CFBundleExecutable and CFBundleIdentifier values. You will also need to personalize the bash script with your user name, Installer ID, Application ID and app specific password. The script will execute the following steps:
- Compile your executable and append a Info.plist section to your executable.
- Generate a disk image.
- Upload your disk image to Apple. Wait for a response (which can take a few minutes).
- Assuming success, staple your ticket to your disk image.
The script notarize.sh will create a universal binary, here is an explanation for this.
Old macOS computers use x86-64 CPUs, while the latest computers use ARM-based Apple Silicon
CPUs. If we want our executable to run natively on both systems, we should compile for each, and use lipo to merge these two into a single universal binary:
g++ -o exeX86 hello.cpp -target x86_64-apple-macos10.12 -mmacosx-version-min=10.12
g++ -O3 -o exeARM hello.cpp -target arm64-apple-macos11 -mmacosx-version-min=11.0
strip ./exeARM; strip ./exeX86
lipo -create -output exe exeARM exeX86
The script notarize.sh will create attach a Info.plist to the executable, here is an explanation for this.
Here we consider a very simple command line tool that we would traditionally compile with the command
g++ -I. hello.cpp -o hello
Apple notarization expects a XML file named 'Info.plist' (it may not be required unless you use features like the camera). For graphical applications distributed as an .app bundle, this file is distributed as a stand-alone file. However, command line tools do not have app bundles, so we need to insert this XML file directly into our executable. Consider a minimal Info.plist (individual projects will need to modify the CFBundleName, CFBundleExecutable and CFBundleIdentifier):
<?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>hello</string>
<key>CFBundleIdentifier</key>
<string>com.mycompany.hello</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>hello</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>CFBundleSupportedPlatforms</key>
<array>
<string>MacOSX</string>
</array>
<key>CFBundlePackageType</key>
<string>APPL</string>
</dict>
</plist>
We can inject this into our application by telling the linker to add this as a section:
g++ -sectcreate __TEXT __info_plist Info.plist -I. hello.cpp -o hello
Alternatively, many people use CMake to compile C applications. Unfortunately, searching the web for instructions for combining Info.plist files using CMake provides a lot of false leads. In the past, most instructions focused on inserting Info.plist files into application bundles. In contrast, we want to inject the file directly into our executable. To solve this, you will want to modify the CMakeLists.txt
file that is in the same folder as the source code and Info.plist file to include:
if(APPLE)
message("-- Adding Apple plist")
set_target_properties(hello PROPERTIES LINK_FLAGS "-Wl,-sectcreate,__TEXT,__info_plist,${CMAKE_SOURCE_DIR}/Info.plist")
endif()
This sample project includes both a CMake file that includes this.
You will need update the script with your private values.
COMPANY_NAME=mycompany
APP_NAME=hello
APP_SPECIFIC_PASSWORD=abcd-efgh-ijkl-mnop
APPLE_ID_USER=myname@gmail.com
APPLE_ID_INSTALL="Developer ID Installer: My Name"
APPLE_ID_APP="Developer ID Application: My Name"
From the comman line, change directory (cd
) to the directory containing notarize.sh and run using ./notarize.sh
. If the process was successful you will have the notarized package hello.pkg
.
Apple has not provided clear documentation or tools for users who do not use XCode to automate the process (and even that has changed over the years). Unfortunately, notarization requirements and details have changed over time. This makes searching for instructions perilous, as methods that worked in the past may no longer work.
- Examples of tools that do not work as expected in late 2020 (
productbuild
) and those that do (pkgbuild
). Unfortunately, when Apple's code-signing and notarization tools do not work as expected, there is little documentation to help, and older documentation is often misleading. - scriptingosx from 2019 describes using XCode.
- eclecticlight from 2019 and 2020 describes using Packages.