Open kentbull opened 1 month ago
I was able to get a workaround functional for pysodium by modifying the DYLD_LIBRARY_PATH
and LD_LIBRARY_PATH
variables as well as symlinking to the correct binary in a libsodium
library directory in my repository.
This works because the entire app repo is copied to the app cache directory of ~/Library/Caches/myapp/app/
when running the app from the .dmg
I build after making flet build macos
.
Here's sample code of what I had to do to get pysodium
to load my libsodium
dynamic library from the libsodium/
directory in my repository.
################################# Custom Libsodium Loader ############################################
# This code has to be in the main module to avoid a partially initialized module error
# for the main app module.
def load_custom_libsodium(appdir):
"""
Instruct the pysodium library to load a custom libsodium dylib from the appdir/libsodium
"""
set_load_path_or_link(appdir)
set_load_env_vars(appdir)
custom_path = os.path.expanduser(f'{os.path.dirname(os.path.abspath(__file__))}/libsodium/libsodium.dylib')
logger.info(f'Loading custom libsodium from {custom_path}')
if os.path.exists(custom_path):
logger.info(f'Found custom libsodium at {custom_path}')
sodium = ctypes.cdll.LoadLibrary(custom_path)
else:
logger.info('Custom libsodium not found, loading from system')
libsodium_path = find_library('sodium')
if libsodium_path is not None:
logger.info(f'Found libsodium at {libsodium_path}')
sodium = ctypes.cdll.LoadLibrary(libsodium_path)
logger.info(f'Loaded libsodium from {libsodium_path}')
else:
raise OSError('libsodium not found')
def set_load_path_or_link(appdir):
"""
Symlinks the correct libsodium dylib based on the architecture of the system.
"""
lib_home = f'{appdir}/libsodium'
arch = platform.processor()
if platform.system() == 'Windows':
match arch:
case 'x86' | 'i386' | 'i486' | 'i586' | 'i686':
sodium_lib = 'libsodium.26.x32.dll'
case 'AMD64' | 'x86_64':
sodium_lib = 'libsodium.26.x64.dll'
case _:
raise OSError(f'Unsupported Windows architecture: {arch}')
elif platform.system() == 'Darwin':
match platform.processor():
case 'x86_64':
sodium_lib = 'libsodium.26.x86_64.dylib'
case 'arm' | 'arm64' | 'aarch64':
sodium_lib = 'libsodium.23.arm.dylib'
# doesn't work
case 'i386':
sodium_lib = 'libsodium.23.i386.dylib'
case _:
raise OSError(f'Unsupported architecture: {platform.processor()}')
else:
# Linux and other Unix-like systems
sodium_lib = 'libsodium.so.23' # not yet supported
raise OSError(f'Unsupported architecture: {platform.processor()}')
lib_path = Path(os.path.join(lib_home, sodium_lib))
logger.info(f'Arch: {platform.processor()} Linking libsodium lib: {sodium_lib} at path: {lib_path}')
if platform.system() == 'Windows': # if windows just set the PATH
logger.info(f'Setting PATH to include {lib_path}')
os.environ['PATH'] = f'{lib_path};{os.environ["PATH"]}'
elif platform.system() == 'Darwin': # if macOS, symlink the dylib
if not lib_path.exists():
logger.error(f'libsodium for architecture {platform.processor()} missing at {lib_path}, cannot link')
raise FileNotFoundError(f'libsodium for architecture {platform.processor()} missing at {lib_path}')
link_path = Path(os.path.join(lib_home, 'libsodium.dylib'))
logger.info(f'Symlinking {lib_path} to {link_path}')
try:
os.symlink(f'{lib_path}', f'{link_path}')
except FileExistsError:
os.remove(f'{link_path}')
os.symlink(f'{lib_path}', f'{link_path}')
logger.info(f'Linked libsodium dylib: {link_path}')
def set_load_env_vars(appdir):
"""
Sets the DYLD_LIBRARY_PATH and LD_LIBRARY_PATH that pysodium uses to find libsodium to the custom libsodium dylib.
"""
if platform.system() == 'Windows':
return # Windows doesn't need this
local_path = appdir
logger.info(f'Setting DYLD_LIBRARY_PATH to {local_path}/libsodium')
os.environ['DYLD_LIBRARY_PATH'] = f'{local_path}/libsodium'
logger.info(f'Setting LD_LIBRARY_PATH to {local_path}/libsodium')
os.environ['LD_LIBRARY_PATH'] = f'{local_path}/libsodium'
################################### End Custom Libsodium Loader ######################################
This is just for MacOS machines. I haven't tested the Windows support yet.
Static linking would completely remove the need to do this dynamic module loading.
Duplicate Check
Describe the requested feature
What is the recommended approach to linking a needed system library into my Flet app?
I depend on libsodium (through pysodium), a popular cryptography library, at /usr/local/lib/libsodium.23.dylib yet get the runtime code signing error
This is after doing the codesigning and notarization to build my .dmg file for distribution.
I see the Flutter discussion of Flutter tools support for packaging/linking to native libraries and am wondering if I need to take some FFI approach or just include the libsodium.23.dylib as a part of my build and codesigning process, as in
This seems a bit heavyweight, though if that's what I must do, then that's what I will do.
A relevant issue from the Flutter repo is https://github.com/flutter/flutter/issues/33227 (Flutter tools support for packaging/linking to native libraries)
This flet issue is probably related: https://github.com/flet-dev/flet/issues/2823 (libmpv.so.1 not found (when libmpv is already installed) - fixable but hacky)
Suggest a solution
I suggest creating a strong recommendation in the Flet documentation to do static linking: downloading and building dependent libraries for each target architecture and linking them to the built Flutter app during the build process.
Screenshots
No response
Additional details
No response