Code signing C++ app with embedded Python3 interpreter

Sigil (https://github.com/Sigil-Ebookl/Sigil) is a Qt cross-platform epub2 and epub3 editor on Mac OS X / Windows / Linux. It embeds a relocatable Python3 framework inside Sigil.app on OSX.


The OS X Code signing works for Sigil.app and for the dmg used to distribute it but upon first launch the internal embedded Python3 interpreter creates *.pyc compiled python code files inside various __pycache__ directories inside the Python.framework stored inside the Sigil.app.


This causes further code-sign testing to fail with "files added messages" error messages as follows:


codesign --verify --deep --strict --verbose=2 Sigil.app

--prepared:/Applications/Sigil.app/Contents/Frameworks/Python.framework/Versions/Current/.

Sigil.app: a sealed resource is missing or invalid

In subcomponent: /Applications/Sigil.app/Contents/Frameworks/Python.framework

file added: /Applications/Sigil.app/Contents/Frameworks/Python.framework/Versions/Current/lib/python3.5/__pycache__/__future__.cpython-35.pyc

file added: /Applications/Sigil.app/Contents/Frameworks/Python.framework/Versions/Current/lib/python3.5/__pycache__/_bootlocale.cpython-35.pyc

file added: /Applications/Sigil.app/Contents/Frameworks/Python.framework/Versions/Current/lib/python3.5/__pycache__/_collections_abc.cpython-35.pyc

file added: /Applications/Sigil.app/Contents/Frameworks/Python.framework/Versions/Current/lib/python3.5/__pycache__/_compression.cpython-35.pyc

...

When the exact same codesign test is run on the distribution dmg and just installed app before it is first run, it passes completely as these cache compiled bytecode files are not generated until then.

Later launches do still work and there is no "abort". Is this a signing error? Or is signing mainly used only when the application is first launched.

Is there any way around it except for precompiling all .py files used internally in Python3's interpreter to create .pyc files and removing all .py files?

Is there a way to make code signing ignore things like __pycache__ directories stored in embedd frameworks changing?

I could prevent the .pyc creation with the Py_DontWriteBytecodeFlag = 1; when creating the mebedded interpreter but I assume this would hurt python3 performance greatly as the interpreter's internal constantly reused files would have to be recompiled for every single use.

Ideas?

Answered by kbhend in 800011022

I gave up and spent about 3 hours googling around and found a project that had successfully codesigned an embedded Python.framework and I was able to adapt it to actually work.

In case others run into this issue here is the script:

cd bin

# Use Depth First

# Python Frameworks

find ./Sigil.app/Contents/Frameworks/Python.framework/Versions/3.11/lib/ -type f -perm -u=x -exec codesign --force --verbose --options=runtime --entitlements=/Users/kbhend/entitlements.plist --timestamp -s "${CODE_SIGN_ID}" {} \;
find ./Sigil.app/Contents/Frameworks/Python.framework/Versions/3.11/bin/ -type f -perm -u=x -exec codesign --force --verbose --options=runtime --entitlements=/Users/kbhend/entitlements.plist --timestamp -s "${CODE_SIGN_ID}" {} \;
find ./Sigil.app/Contents/Frameworks/Python.framework/Versions/3.11/lib/ -type f -name "*dylib" -exec codesign --force --verbose --options=runtime --entitlements=/Users/kbhend/entitlements.plist --timestamp -s "${CODE_SIGN_ID}" {} \;
find ./Sigil.app/Contents/Frameworks/Python.framework/Versions/3.11/lib/ -type f -name "*so" -exec codesign --force --verbose --options=runtime --entitlements=/Users/kbhend/entitlements.plist --timestamp -s "${CODE_SIGN_ID}" {} \;
find ./Sigil.app/Contents/Frameworks/Python.framework/Versions/3.11/lib/ -type f -name "*.a" -exec codesign --force --verbose --options=runtime --entitlements=/Users/kbhend/entitlements.plist --timestamp -s "${CODE_SIGN_ID}" {} \;
find ./Sigil.app/Contents/Frameworks/Python.framework/Versions/3.11/lib/ -type f -name "*libitclstub*" -exec codesign --force --verbose --options=runtime --entitlements=/Users/kbhend/entitlements.plist --timestamp -s "${CODE_SIGN_ID}" {} \;
codesign --force --options=runtime --entitlements=/users/kbhend/entitlements.plist --deep --verbose --timestamp -s "${CODE_SIGN_ID}" ./Sigil.app/Contents/Frameworks/Python.framework/Versions/3.11/Resources/Python.app
codesign --force --deep --verbose -s "${CODE_SIGN_ID}" --options=runtime --entitlements=/Users/kbhend/entitlements.plist --timestamp ./Sigil.app/Contents/Frameworks/Python.framework

Is there a way to make code signing ignore things like

__pycache__
directories stored in embedd frameworks changing?

Historically that was possible — and the support is probably still there in the OS — but it’s strongly discouraged these days.

Is there any way around it except for precompiling all

.py
files used internally in Python3's interpreter to create
.pyc
files and removing all
.py
files?

My recommendation here is that you try to convince Python to use a different directory hierarchy for storing the

.pyc
files. I don’t know enough about Python to offer specific instructions for this but I’d be very surprised if Python didn’t offer such an option (it would come in handy, for example, when running off read-only media).

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

It is now many years later and I am still trying to sign Sigil well enough to get it notarized.

I have tried everything and not matter what I do when code-signing the embedded Python.framework inside Sigil.app Frameworks, I get the following error:

--prepared:/Volumes/SSD-Drive/repo/devsigil/build672/bin/Sigil.app/Contents/Frameworks/Python.framework/Versions/Current/. ./Sigil.app: a sealed resource is missing or invalid In subcomponent: /Volumes/SSD-Drive/repo/devsigil/build672/bin/Sigil.app/Contents/Frameworks/Python.framework

file modified: /Volumes/SSD-Drive/repo/devsigil/build672/bin/Sigil.app/Contents/Frameworks/Python.framework/Versions/Current/lib/libtcl8.6.dylib file modified: /Volumes/SSD-Drive/repo/devsigil/build672/bin/Sigil.app/Contents/Frameworks/Python.framework/Versions/Current/lib/libtk8.6.dylib file modified: /Volumes/SSD-Drive/repo/devsigil/build672/bin/Sigil.app/Contents/Frameworks/Python.framework/Versions/Current/lib/python3.11/lib-dynload/_asyncio.cpython-311-darwin.so file modified: /Volumes/SSD-Drive/repo/devsigil/build672/bin/Sigil.app/Contents/Frameworks/Python.framework/Versions/Current/lib/python3.11/lib-dynload/_bisect.cpython-311-darwin.so file modified: /Volumes/SSD-Drive/repo/devsigil/build672/bin/Sigil.app/Contents/Frameworks/Python.framework/Versions/Current/lib/python3.11/lib-dynload/_blake2.cpython-311-darwin.so file modified: /Volumes/SSD-Drive/repo/devsigil/build672/bin/Sigil.app/Contents/Frameworks/Python.framework/Versions/Current/lib/python3.11/lib-dynload/_bz2.cpython-311-darwin.so file modified: /Volumes/SSD-Drive/repo/devsigil/build672/bin/Sigil.app/Contents/Frameworks/Python.framework/Versions/Current/lib/python3.11/lib-dynload/_codecs_cn.cpython-311-darwin.so file modified: /Volumes/SSD-Drive/repo/devsigil/build672/bin/Sigil.app/Contents/Frameworks/Python.framework/Versions/Current/lib/python3.11/lib-dynload/_codecs_hk.cpython-311-darwin.so ...

The interesting thing is that Current is actually a symlink to the "3.11" folder.

My signing script never uses that symlink but rpaths might and so might Python internally.

I use a Mac OS Extended Journal Case Sensitive file system if that matters. I did look online for help and there are no file names using accents that many people reported trouble with.

As recommended I use a depth first approach to signing and do not use --deep anyplace. An example of how I depth first sign is:

codesign -s "${CODE_SIGN_ID}" --options=runtime --entitlements=/Users/kbhend/entitlements.plist --timestamp ./Sigil.app/Contents/Frameworks/Python.framework/Versions/3.11/lib/libtk8.6.dylib

Notice I do not use the Current symlink in the actual codesign.

Somehow codesign is getting confused by the symlink?

Any ideas how to debug this failure further?

Accepted Answer

I gave up and spent about 3 hours googling around and found a project that had successfully codesigned an embedded Python.framework and I was able to adapt it to actually work.

In case others run into this issue here is the script:

cd bin

# Use Depth First

# Python Frameworks

find ./Sigil.app/Contents/Frameworks/Python.framework/Versions/3.11/lib/ -type f -perm -u=x -exec codesign --force --verbose --options=runtime --entitlements=/Users/kbhend/entitlements.plist --timestamp -s "${CODE_SIGN_ID}" {} \;
find ./Sigil.app/Contents/Frameworks/Python.framework/Versions/3.11/bin/ -type f -perm -u=x -exec codesign --force --verbose --options=runtime --entitlements=/Users/kbhend/entitlements.plist --timestamp -s "${CODE_SIGN_ID}" {} \;
find ./Sigil.app/Contents/Frameworks/Python.framework/Versions/3.11/lib/ -type f -name "*dylib" -exec codesign --force --verbose --options=runtime --entitlements=/Users/kbhend/entitlements.plist --timestamp -s "${CODE_SIGN_ID}" {} \;
find ./Sigil.app/Contents/Frameworks/Python.framework/Versions/3.11/lib/ -type f -name "*so" -exec codesign --force --verbose --options=runtime --entitlements=/Users/kbhend/entitlements.plist --timestamp -s "${CODE_SIGN_ID}" {} \;
find ./Sigil.app/Contents/Frameworks/Python.framework/Versions/3.11/lib/ -type f -name "*.a" -exec codesign --force --verbose --options=runtime --entitlements=/Users/kbhend/entitlements.plist --timestamp -s "${CODE_SIGN_ID}" {} \;
find ./Sigil.app/Contents/Frameworks/Python.framework/Versions/3.11/lib/ -type f -name "*libitclstub*" -exec codesign --force --verbose --options=runtime --entitlements=/Users/kbhend/entitlements.plist --timestamp -s "${CODE_SIGN_ID}" {} \;
codesign --force --options=runtime --entitlements=/users/kbhend/entitlements.plist --deep --verbose --timestamp -s "${CODE_SIGN_ID}" ./Sigil.app/Contents/Frameworks/Python.framework/Versions/3.11/Resources/Python.app
codesign --force --deep --verbose -s "${CODE_SIGN_ID}" --options=runtime --entitlements=/Users/kbhend/entitlements.plist --timestamp ./Sigil.app/Contents/Frameworks/Python.framework

Code signing C++ app with embedded Python3 interpreter
 
 
Q