fman works again on macOS Mojave

fman wouldn't start on macOS Mojave. I just released fman 1.6.9 that fixes this. Because of fman's auto-update mechanism, this caused problems for many people. I'm very sorry for the trouble.

Here's the back story: The latest version of macOS, Catalina, introduced a new requirement called Notarization for app developers. Basically, if you want to publish an application, then you need to send your binaries to Apple for verification before you can give it to your users. Otherwise, anybody who downloads your app gets a very ugly warning "this app needs to be updated by its developer".

To make fman ready for Catalina, I released version 1.6.7 a few days ago. It worked fine on my test systems running macOS Sierra, High Sierra, Mojave and Catalina. Unfortunately, it didn't work for quite a few of fman's users, particularly on Mojave.

The mistake I had made in notarizing fman was that I didn't supply an entitlements file. Here are the gory technical details in case you are a developer who found this page on Google:

fman is a PyQt application. When its users tried to start it on Mojave, the following error occurred. (You can see this by launching your app via the Terminal):

$ /Applications/fman.app/Contents/MacOS/fman

Traceback (most recent call last):
  File "PyInstaller/loader/pyiboot01_bootstrap.py", line 127, in 
  File "", line 971, in _find_and_load
  File "", line 955, in _find_and_load_unlocked
  File "", line 665, in _load_unlocked
  File ".../PyInstaller/loader/pyimod03_importers.py", line 627, in exec_module
  File "ctypes/__init__.py", line 538, in 
  File "ctypes/__init__.py", line 273, in _reset_cache
MemoryError
[13379] Failed to execute script pyiboot01_bootstrap

The fix for this is to supply an entitlements file when code signing your application. Here is the file which fman uses:

<?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>
		<!-- Avoid ctypes MemoryError: -->
		<key>com.apple.security.cs.allow-unsigned-executable-memory</key>
		<true/>
		<!-- Required for loading unsigned .so files from plugins: -->
		<key>com.apple.security.cs.disable-library-validation</key>
		<true/>
	</dict>
</plist>

You save this to a file (say entitlements.plist) and supply it when invoking the codesign command:

codesign ... --entitlements entitlements.plist ...

As you can see in the code comment above, the allow-unsigned-executable-memory is required to fix the MemoryError. In addition to this, fman requires the disable-library-validation entitlement. The reason for this is that fman has a plugin system and plugins may load (unsigned) .so library files. In particular this entitlement fixes the following error, which I encountered:

ImportError: dlopen(.../osxtrash.cpython-36m-darwin.so, 2): no suitable
image found.

Did find: .../osxtrash.cpython-36m-darwin.so:

code signature in .../osxtrash.cpython-36m-darwin.so not valid for use in
process using Library Validation: mapped file has no cdhash, completely
unsigned? Code has to be at least ad-hoc signed.

Hopefully this helps someone else. If you are a PyQt developer, you may be interested in fman's build system. It hugely simplifies the deployment and packaging of PyQt apps.

Michael started fman in 2016, convinced that we deserve a better file manager. fman's launch in 2017 was a huge success. But despite full-time work, it only makes $500 per month. The goal is to fix this.