From 0d6b4a39e57bac247d3dfb6a617b93abbf5086ff Mon Sep 17 00:00:00 2001 From: brandon wright <33914549+bwrightkc@users.noreply.github.com> Date: Mon, 20 Jul 2026 13:26:04 -0500 Subject: [PATCH 1/2] Bundle libsharpyuv for macOS py2app build py2app doesn't rewrite @rpath load commands, so libwebp's dependency on libsharpyuv (split out since webp 1.3) goes missing from the app bundle and PIL fails to import at launch. Bundle and patch it explicitly, same as the freecell-solver lib. --- setup_osx.py | 33 +++++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/setup_osx.py b/setup_osx.py index 9ba0da897..584fe0ff0 100644 --- a/setup_osx.py +++ b/setup_osx.py @@ -6,7 +6,7 @@ import os import shutil import sys -from subprocess import call +from subprocess import CalledProcessError, call, check_output from pysollib.settings import PACKAGE, VERSION @@ -36,6 +36,19 @@ SOLVER_LIB_PATH = None SOLVER = [] +# libwebp (a Pillow dependency) depends on libsharpyuv via a bare +# @rpath reference that py2app's dependency walker doesn't follow. +# Bundle it explicitly, same as the solver lib above. +SHARPYUV_LIB_PATH = None +try: + _webp_prefix = check_output( + ["brew", "--prefix", "webp"]).decode().strip() + _candidate = os.path.join(_webp_prefix, "lib", "libsharpyuv.0.dylib") + if os.path.exists(_candidate): + SHARPYUV_LIB_PATH = _candidate +except (OSError, CalledProcessError): + pass + GETINFO_STRING = "PySol Fan Club Edition \ %s %s, (C) 1998-2003 Markus F.X.J Oberhumer \ (C) 2006-2007 Skomoroh" % (PACKAGE, VERSION) @@ -55,7 +68,8 @@ DATA_FILES = ['docs', 'data', 'locale', 'scripts', 'COPYING', 'README.md', ] + SOLVER RESOURCES = [] -FRAMEWORKS = [SOLVER_LIB_PATH] if SOLVER_LIB_PATH else [] +FRAMEWORKS = ([SOLVER_LIB_PATH] if SOLVER_LIB_PATH else []) + \ + ([SHARPYUV_LIB_PATH] if SHARPYUV_LIB_PATH else []) # with argv_emulation=True, the app window is not shown when launched OPTIONS = dict(argv_emulation=False, emulate_shell_environment=True, @@ -84,3 +98,18 @@ @executable_path/../Frameworks/libfreecell-solver.0.dylib fc-solve", shell=True ) + os.chdir(top) + +# Point libwebp at the bundled libsharpyuv instead of its broken +# original rpath. +if SHARPYUV_LIB_PATH and "py2app" in sys.argv: + frameworks_dir = os.path.join( + top, 'dist', '%s.app' % PACKAGE, 'Contents', 'Frameworks') + for name in os.listdir(frameworks_dir): + if name.startswith('libwebp'): + call([ + 'install_name_tool', '-change', + '@rpath/libsharpyuv.0.dylib', + '@executable_path/../Frameworks/libsharpyuv.0.dylib', + os.path.join(frameworks_dir, name), + ]) From 0a7d5b0b57f9c277c70f13f18d125077a38b57a4 Mon Sep 17 00:00:00 2001 From: brandon wright <33914549+bwrightkc@users.noreply.github.com> Date: Mon, 20 Jul 2026 13:35:32 -0500 Subject: [PATCH 2/2] Re-sign macOS bundle after post-py2app dylib patching install_name_tool invalidates py2app's ad-hoc signature, so a bundle patched after signing (e.g. the libsharpyuv fix, or the existing fc-solve rpath fix) gets silently SIGKILLed by macOS on launch. Verified locally: without this, the fixed app dies with no output at all: the patched dylib breaks the code signature seal. --- setup_osx.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/setup_osx.py b/setup_osx.py index 584fe0ff0..650bccbeb 100644 --- a/setup_osx.py +++ b/setup_osx.py @@ -113,3 +113,12 @@ '@executable_path/../Frameworks/libsharpyuv.0.dylib', os.path.join(frameworks_dir, name), ]) + +# install_name_tool invalidates py2app's ad-hoc signature above, and +# macOS refuses to launch a bundle whose seal doesn't match its +# contents. Re-sign after any post-processing. +if (SOLVER or SHARPYUV_LIB_PATH) and "py2app" in sys.argv: + call([ + 'codesign', '--force', '--deep', '--sign', '-', + os.path.join(top, 'dist', '%s.app' % PACKAGE), + ])