ronaldoussoren / py2app

py2app is a Python setuptools command which will allow you to make standalone Mac OS X application bundles and plugins from Python scripts.
Other
359 stars 36 forks source link

Incorrect path to libtorch.dylib (with possible solution) #482

Open octimot opened 1 year ago

octimot commented 1 year ago

Hello!

I'm trying to freeze an app that has some functions which need torch and torchaudio. Unfortunately, I get the following error when running the app:

OSError: dlopen(***********/dist/TorchTest.app/Contents/Resources/lib/python3.9/torchaudio/lib/libtorchaudio.so, 0x0006): Library not loaded: '@rpath/libtorch.dylib'
  Referenced from: '***********/dist/TorchTest.app/Contents/Resources/lib/python3.9/torchaudio/lib/libtorchaudio.so'
  Reason: tried: '/usr/local/lib/libtorch.dylib' (no such file), '/usr/lib/libtorch.dylib' (no such file)

(I've edited out the full path ***)

To simplify, I've tried to compile this snipped of code only (torchtest.py), but it returns the same error:

import torch
import torchaudio
import librosa
import onnxruntime
import resampy

print('Loading audio file...')

audio_array, sr = librosa.load('example_vad.wav', sr=16_000)

print('Loading silero model...')

# load the silero model
vad_model, utils = torch.hub.load(repo_or_dir='snakers4/silero-vad', model='silero_vad', force_reload=False,
                                  onnx=True, trust_repo=True, verbose=False)
(get_speech_timestamps, _, read_audio, _, collect_chunks) = utils

# convert the audio_segment to a torch tensor
audio_segment_torch = torch.from_numpy(audio_array)

print('Running silero model...')

speech_timestamps = get_speech_timestamps(audio_segment_torch, vad_model,
                                          sampling_rate=16_000,
                                          window_size_samples=512,
                                          speech_pad_ms=200,
                                          threshold=0.5
                                          )

print('Done!')
print(speech_timestamps)

And this is the setup.py I'm using when doing python setup.py py2app:

from setuptools import setup

import sys
sys.setrecursionlimit(10000)

APP = [{'script': 'torchtest.py'}]

DATA_FILES = ['example_vad.wav']

# all the frameworks compiled for M1
frameworks = [
            # libffi from homebrew
            '/opt/homebrew/Cellar/libffi/3.4.2/lib/libffi.8.dylib',
            '/opt/homebrew/Cellar/libffi/3.4.2/lib/libffi.7.dylib',
            '/opt/homebrew/Cellar/libffi/3.4.2/lib/libffi.dylib',

            # trying to load from venv 
            #(files are copied in the Frameworks dir, but app doesn't look for them there I think
            'venv/lib/python3.9/site-packages/torch/lib/libc10.dylib',
            'venv/lib/python3.9/site-packages/torch/lib/libshm.dylib',
            'venv/lib/python3.9/site-packages/torch/lib/libtorch_cpu.dylib',
            'venv/lib/python3.9/site-packages/torch/lib/libtorch_global_deps.dylib',
            'venv/lib/python3.9/site-packages/torch/lib/libtorch_python.dylib',
            'venv/lib/python3.9/site-packages/torch/lib/libtorch.dylib'
           ]

OPTIONS = {
    'frameworks': frameworks,
    'includes': [],
    'packages': ['cffi', 'librosa', 'torch', 'torchaudio', 'resampy', 'soundfile', '_soundfile'],
    'excludes': ['PyInstaller', 'py2app'],
    'plist': {
        'LSUIElement': False,
        'NSRequiresAquaSystemAppearance': False,
        'CFBundleName': 'TorchTest',
        'CFBundleDisplayName': 'TorchTest',
        'CFBundleIdentifier': 'com.test.TorchTest',
        'CFBundleGetInfoString': 'TorchTest',
        'CFBundleVersion': '0.0.1',
        'CFBundleShortVersionString': '0.0.1'
    }
}

setup(
    app=APP,
    name ='TorchTest',
    version = "{}".format(version.__version__),
    data_files=DATA_FILES,
    options={'py2app': OPTIONS},
    setup_requires=['py2app'],
)

I'm running py2app on Mac OS 12.6 (M1).

It seems that despite the dylibs are being copied when I include them in the frameworks list, they are not correctly referenced by ibtorchaudio.so.

Any ideas would be greatly appreciated!

Cheers

octimot commented 1 year ago

Could this be related with the @rpath issue mentioned here https://github.com/ronaldoussoren/py2app/issues/286 and technically fixed here https://github.com/ronaldoussoren/py2app/pull/315 ?

I'd love to help help with improvements, but just need a few pointers to know what I should be looking for.

octimot commented 1 year ago

To make sure that nothing weird is happening due to a packaged python installation, I've cleaned up all conda and homebrew python installs and reinstalled python 3.9.13 using the universal2 installer from pyhton.org.

Unfortunately, the @rpath error still persists.

octimot commented 1 year ago

I managed to manually change the @rpaths to @executable_paths like this:

First, I added the following code that collects all the libtorch related dylibs:

# add all the torch libs to the frameworks list
torch_libs = glob.glob(os.path.join(sys.exec_prefix, '**/libtorch*'), recursive=True)
frameworks += torch_libs

Then, after running python setup.py py2app, I've manually changed the @rpaths using install_name_tool (from Terminal):

install_name_tool -change @rpath/libtorch.dylib @executable_path/../Resources/lib/python3.9/torch/lib/libtorch.dylib dist/TorchTest.app/Contents/Resources/lib/python3.9/torchaudio/lib/libtorchaudio.so
install_name_tool -change @rpath/libtorch_cpu.dylib @executable_path/../Resources/lib/python3.9/torch/lib/libtorch_cpu.dylib dist/TorchTest.app/Contents/Resources/lib/python3.9/torchaudio/lib/libtorchaudio.so
install_name_tool -change @rpath/libc10.dylib @executable_path/../Resources/lib/python3.9/torch/lib/libc10.dylib dist/TorchTest.app/Contents/Resources/lib/python3.9/torchaudio/lib/libtorchaudio.so

Since the signature on libtorchaudio.so is now invalidated, I needed to codesign it again:

codesign -f -s - dist/TorchTest.app/Contents/Resources/lib/python3.9/torchaudio/lib/libtorchaudio.so

Similarly, I found that _torchaudio.so also needs some path changes:

install_name_tool -change @rpath/libtorch.dylib @executable_path/../Resources/lib/python3.9/torch/lib/libtorch.dylib dist/TorchTest.app/Contents/Resources/lib/python3.9/torchaudio/_torchaudio.so
install_name_tool -change @rpath/libtorch_cpu.dylib @executable_path/../Resources/lib/python3.9/torch/lib/libtorch_cpu.dylib dist/TorchTest.app/Contents/Resources/lib/python3.9/torchaudio/_torchaudio.so
install_name_tool -change @rpath/libc10.dylib @executable_path/../Resources/lib/python3.9/torch/lib/libc10.dylib dist/TorchTest.app/Contents/Resources/lib/python3.9/torchaudio/_torchaudio.so
install_name_tool -change @rpath/libtorch_python.dylib @executable_path/../Resources/lib/python3.9/torch/lib/libtorch_python.dylib dist/TorchTest.app/Contents/Resources/lib/python3.9/torchaudio/_torchaudio.so
install_name_tool -change @rpath/libtorchaudio.so @executable_path/../Resources/lib/python3.9/torchaudio/lib/libtorchaudio.so dist/TorchTest.app/Contents/Resources/lib/python3.9/torchaudio/_torchaudio.so

codesign -f -s - dist/TorchTest.app/Contents/Resources/lib/python3.9/torchaudio/_torchaudio.so

BTW, to find out which paths need to be changed, I simply ran otool on the affected files inside the bundle:

otool -L dist/TorchTest.app/Contents/Resources/lib/python3.9/torchaudio/lib/libtorchaudio.so
otool -L dist/TorchTest.app/Contents/Resources/lib/python3.9/torchaudio/_torchaudio.so

What would be the best approach to incorporate this into py2app?

Tokarak commented 1 year ago

This is because #315 has been reverted THis is false, the code has moved to https://github.com/ronaldoussoren/py2app/blob/master/src/py2app/apptemplate/setup.py#L23

I think I found the problem, PR coming up :)

airpdev commented 1 year ago

Hi, @octimot Did you solve this problem? I have faced same issue in using py2app.

Tokarak commented 1 year ago

@fullstack-dev0727 try my fork (#504). Please respond whether it works, it needs tests.

airpdev commented 1 year ago

@Tokarak Thanks for your message. I will test and let you know about the results.

airpdev commented 1 year ago

Hi, @Tokarak What do I have to do exactly to resolve the issue?

Tokarak commented 1 year ago

@fullstack-dev0727 clone the fork and do pip install [path to repo]. Then you can use python -m py2app like usual.

airpdev commented 1 year ago

Hi, @Tokarak Thanks for your reply. We can use py2app for arm64 M1 Mac mini?

{ "name": "main-x86_64", "target": "10.5", "cflags": "-g -arch x86_64 -Wl,-rpath,@executable_path/../Frameworks", "cc": "/usr/bin/clang", },

rpath can be used in only x84_64 machine?

airpdev commented 1 year ago

@Tokarak I set rpath in arm64 manually by calling install_name_tool.

install_name_tool -add_rpath @executable_path/../Frameworks testapp

But app is stopped immediately. Please let me know the solution.

Tokarak commented 1 year ago

the issue I had, rpath was already set, butthe dylibs were not copied into the Frameworks folder (which should be in rpath already). Are there any dylibs in the Contents/Frameworks folder of your compiled app?