TL;DR, I’ve published a script to automate the installation here.
I recently wanted to use AppleGlot, a developer tool from Apple that facilitates the automatic translation of strings from Apple’s published string glossaries from macOS and iOS. A good demonstration of its functionality is available here.
However, the latest version of AppleGlot available from developer.apple.com at time of writing is 4.0 (v161.6), which has the following issues on macOS Catalina and Big Sur:
The signing certificate is invalid (FB8770951) and the installer hasn’t been updated to work on macOS Catalina’s Read-Only System Volume (FB8773764).
I inspected the package file with Suspicious Package, which reveals that the appleglot
binary is installed to /usr/local/bin/appleglot
, whilst several related frameworks attempt to be installed to /System/Library/PrivateFrameworks
– which is no longer possible as of macOS Catalina.
By exporting the appleglot
binary from the package and running otool -l
on the binary (-l
is used to “print the load commands”), we can see that appleglot
attempts to load the dynamically-linked AppleGlot.framework
library from the aforementioned /System/Library/PrivateFrameworks/
:
otool -l appleglot
...
Load command 11
cmd LC_LOAD_DYLIB
cmdsize 56
name /usr/lib/libobjc.A.dylib (offset 24)
time stamp 2 Thu Jan 1 10:00:02 1970
current version 228.0.0
compatibility version 1.0.0
Load command 12
cmd LC_LOAD_DYLIB
cmdsize 104
name /System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation (offset 24)
time stamp 2 Thu Jan 1 10:00:02 1970
current version 635.0.0
compatibility version 150.0.0
Load command 13
cmd LC_LOAD_DYLIB
cmdsize 88
name /System/Library/Frameworks/Cocoa.framework/Versions/A/Cocoa (offset 24)
time stamp 2 Thu Jan 1 10:00:02 1970
current version 17.0.0
compatibility version 1.0.0
Load command 14
cmd LC_LOAD_DYLIB
cmdsize 104
name /System/Library/PrivateFrameworks/AppleGlot.framework/Versions/A/AppleGlot (offset 24)
time stamp 2 Thu Jan 1 10:00:02 1970
current version 161.6.3
compatibility version 1.0.0
To fix this, we can use install_name_tool
to modify the appleglot
binary to load this framework from another file path. install_name_tool
, which is located at /usr/bin/install_name_tool
, is used to “change dynamic shared library install names”.
💡 Fun fact
The install_name_tool
binary, as with other binaries from Xcode.app is a stub that calls the install_name_tool
binary located in the current Xcode toolchain with /usr/lib/libxcselect.dylib
. We can see this with the full disassembly of the binary below:
otool -tV /usr/bin/install_name_tool
/usr/bin/install_name_tool:
(__TEXT,__text) section
_main:
0000000100000f6b pushq %rbp
0000000100000f6c movq %rsp, %rbp
0000000100000f6f leal -0x1(%rdi), %eax
0000000100000f72 leaq 0x8(%rsi), %rdx
0000000100000f76 leaq 0x29(%rip), %rdi ## literal pool for: "install_name_tool"
0000000100000f7d movl %eax, %esi
0000000100000f7f xorl %ecx, %ecx
0000000100000f81 callq 0x100000f86 ## symbol stub for: _xcselect_invoke_xcrun
Anyway, back to appleglot
. Let’s copy the frameworks into a path that is not limited by System Integrity Protection – I’ve chosen /Library/Frameworks
. Then, we can copy the appleglot
binary and man page into their directories in /usr
, and the Appleglot plugins to /Library/Application Support/AppleGlot
. These items in the installer package can be they can be coped from Suspicious Package, or from the output of pkgutil --expand AppleGlot.pkg <folder>
Then, we can use install_name_tool
to change where the appleglot binary loads its frameworks:
install_name_tool \
./usr/local/bin/appleglot \
-change \
"/System/Library/PrivateFrameworks/AppleGlot.framework/Versions/A/AppleGlot" \
"/Library/Frameworks/AppleGlot.framework/Versions/A/AppleGlot" \
We also need to update the load paths for each of the AppleGlot Frameworks’s plugins (I’m not sure what the plugins in the “PlugIns Disabled” are for, but in my basic usage of appleglot
they haven’t appeared to be used).
for filename in /Library/Frameworks/AppleGlot.framework/PlugIns/*; do
BUNDLE_NAME="$(basename $filename)"
BINARY_NAME="${BUNDLE_NAME%.*}"
install_name_tool \
"$filename/Contents/MacOS/$BINARY_NAME" \
-change \
"/System/Library/PrivateFrameworks/AppleGlot.framework/Versions/A/AppleGlot" \
"/Library/Frameworks/AppleGlot.framework/Versions/A/AppleGlot"
install_name_tool \
"$filename/Contents/MacOS/$BINARY_NAME" \
-change \
"/System/Library/PrivateFrameworks/MonteLib.framework/Versions/A/MonteLib" \
"/Library/Frameworks/MonteLib.framework/Versions/A/MonteLib"
done
And then it works!
appleglot -h
Usage: appleglot [options] command [arguments]
appleglot commands are:
list print list of components in NewBase
getlangs get the current base language and target language
setlangs set the current base language and target language
populate create NewLoc, using OldLoc if available
update update NewLoc from Glossaries
finalize remove temporary working files
create create empty environment
'appleglot -h command_name' prints the description of 'command_name'.
Options:
-a Set append mode for log files.
-f Force update mode (disable clean population logic).
-b path_to_budnle Specify the bundle path for nibtool/ibtool.
-d path_to_AG_env Specify the path for the AppleGlot environment.
Default is the current directory.
-n path_to_nibtool Specify the path for nibtool.
-F paths_to_frameworks Specify custom framework paths (colon separated) for nibtool.
-L paths_to_libraries Specify custom library paths (colon separated) for nibtool.
-p path_to_palette Specify the path for IB palette for nibtool.
-P path_to_directory Specify the path for IB palette directory for nibtool.
-g path_to_plugin Specify the path for IB plugin for ibtool.
-G path_to_directory Specify the path for IB plugin directory for ibtool.
-w Use AD/WG for glossary files.
-y Use XLIFF for glossary files.
Thanks for reading, and feel free to use the bash script to automate this install process. Hopefully this blog post can be deprecated soon 🤞