jellyfin / jellyfin-media-player

Jellyfin Desktop Client
GNU General Public License v2.0
3.11k stars 311 forks source link

libsharpyuv missing when building on latest macos not limited to qt6 #536

Open sevenrats opened 9 months ago

sevenrats commented 9 months ago

libyuvsharp.0.dylib is not missing. It's sitting right in brew libs like its supposed to be but it does not get packaged. I haven't tried to fix it so I don't know if its going to be a single dep or a full hell but several other projects based on mpv have noted very similar issues: https://github.com/mpv-player/mpv/issues/11897 https://github.com/mpv-player/mpv/issues/10387

examining the binary with otool shows know mention of libyuvsharp or the libraries mentioned above but I will go through the fixes mentioned in the linked issues and find what works.

sevenrats commented 9 months ago

libsharpyuv is not packaged properly, but its links are set properly as if it is. moving it into place gets past the issue. librubberband and libleptonica have dependencies links that start with @loader_path/../../../../opt these need to be changed to $(homebrew --prefix) well test this evening and see if there are any other isues.

sevenrats commented 9 months ago

modifying fix-install-names.py like this fixes it, but I'm not sure if its reasonable to pull this in or not. perhaps this is just a temporary packaging bug upstream. will just wait and see

from pathlib import Path
import sys
import argparse
from collections import deque
import subprocess
import shutil

def main(argv=tuple(sys.argv[1:])):
    arg_parser = argparse.ArgumentParser(description='Fix third party library paths in .app bundles.')
    arg_parser.add_argument('bundle', metavar='BUNDLE', type=str, nargs=1)
    arguments = arg_parser.parse_args(argv)

    bundle_path = Path(arguments.bundle[0])
    framework_path = bundle_path / 'Contents' / 'Frameworks'
    framework_libs = set(file.name for file in framework_path.glob('*.dylib')).union(set(file.name for file in framework_path.glob('*.so')))
    libs_to_fix = deque()
    libs_to_fix.extend(file for file in bundle_path.glob('**/*.dylib'))
    libs_to_fix.extend(file for file in bundle_path.glob('**/*.so'))
    while libs_to_fix:
        lib = libs_to_fix.popleft()
        print(lib)
        # find the dependencies of the library
        result = subprocess.check_output(['otool', '-L', str(lib.resolve())], stderr=subprocess.STDOUT).decode('utf-8')
        print(f"\t{result}")
        for dependency in result.splitlines():
            dependency = dependency.strip().lstrip()
            if dependency.startswith('/usr/local'):
                # cut off trailing compatibility string
                dependency = Path(dependency.split(' (compatibility')[0])

                # if somehow macdeployqt didn't copy the lib for us, we do a manual copy
                if dependency.name not in framework_libs:
                    shutil.copy(str(dependency.resolve()), str(framework_path.resolve()))
                    framework_libs.add(dependency.name)
                    # add the newly added library in to the to fix queue
                    libs_to_fix.append(framework_path / dependency.name)
                    print((framework_path / dependency.name).resolve())
                    print(f'Copied {dependency} to {framework_path / dependency.name}')
                # now we fix the path using install_name_tool
                target = f'@executable_path/../Frameworks/{dependency.name}'
                print(f'Fixing dependency {dependency} of {lib} to {target}')
                subprocess.run(['install_name_tool', '-id', target, lib])
                subprocess.run(['install_name_tool', '-change', str(dependency), target, lib])
            # Issue 536
            token = '@loader_path/../../../../'
            if dependency.startswith(token):
                dependency = Path(dependency.split(' (compatibility')[0])
                subPath = str(dependency).replace('@loader_path/../../../../', '')
                actual_path = Path('/opt/homebrew')/subPath
                shutil.copy(actual_path, framework_path.resolve())
                framework_libs.add(dependency.name)
                # add the newly added library in to the to fix queue
                libs_to_fix.append(framework_path / dependency.name)
                print((framework_path / dependency.name).resolve())
                print(f'Copied {dependency} to {framework_path / dependency.name}')
                target = f'@executable_path/../Frameworks/{dependency.name}'
                print(f'Fixing dependency {dependency} of {lib} to {target}')
                subprocess.run(['install_name_tool', '-id', target, lib])
                subprocess.run(['install_name_tool', '-change', str(dependency), target, lib])
            # /Issue 536

if __name__ == '__main__':
    main()
sevenrats commented 9 months ago

this is not limited to qt6, the above script needs to be modified to account for both 5 and 6.