File: fix_bundle.py

package info (click to toggle)
audacity 3.7.3%2Bdfsg-1
  • links: PTS, VCS
  • area: main
  • in suites: trixie
  • size: 125,252 kB
  • sloc: cpp: 358,238; ansic: 75,458; lisp: 7,761; sh: 3,410; python: 1,503; xml: 1,385; perl: 854; makefile: 122
file content (129 lines) | stat: -rw-r--r-- 4,265 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
import argparse
import os
import subprocess
import re

def is_system_lib(path):
    return path.startswith('/System/Library/') or path.startswith('/usr/lib/')


def macos_collect_rpaths(file):
    result = []

    with subprocess.Popen(['otool', '-l', file], stdout=subprocess.PIPE) as p:
        lines = [line.decode('utf-8').strip() for line in p.stdout.readlines()]
        for line_index in range(len(lines)):
            if lines[line_index] == 'cmd LC_RPATH':
                rpath_match = re.match(r'path\s+(.*)\s+\(', lines[line_index + 2])
                if rpath_match:
                    rpath = rpath_match.group(1)
                    if rpath not in result:
                        result.append(rpath)

            line_index = line_index + 1

    return result


def get_dylib_id(file):
    with subprocess.Popen(['otool', '-D', file], stdout=subprocess.PIPE) as p:
        lines = [line.decode('utf-8').strip() for line in p.stdout.readlines()]
        if len(lines) == 2:
            return lines[1].split('/')[-1]
    return ''


def macos_collect_dependencies(file):
    result = []
    dylib_id = get_dylib_id(file)

    with subprocess.Popen(['otool', '-L', file], stdout=subprocess.PIPE) as p:
        lines = [line.decode('utf-8').strip() for line in p.stdout.readlines()]
        for line in lines:
            match = re.match(r'(.*)\s+\(', line)

            if not match:
                continue

            lib_line = match.group(1)
            if not lib_line.startswith('@loader_path') and not is_system_lib(lib_line):
                name = lib_line.split('/')[-1]
                if name != dylib_id:
                    result.append((name, lib_line))

    return result


def run_install_name_tool(file, dep_path_prefix):
    dependencies = macos_collect_dependencies(file)
    rpaths = macos_collect_rpaths(file)

    if len(dependencies) == 0 and len(rpaths) == 0:
        return

    install_name_tool = ['install_name_tool']

    for name, lib_line in dependencies:
        install_name_tool.append('-change')
        install_name_tool.append(lib_line)
        install_name_tool.append(dep_path_prefix + '/' + name)

    for rpath in rpaths:
        install_name_tool.append('-delete_rpath')
        install_name_tool.append(rpath)

    install_name_tool.append(file)

    print(' '.join(install_name_tool))
    try:
        subprocess.check_call(install_name_tool)
    except subprocess.CalledProcessError as err:
        print(f'=========\ninstall_name_tool failed with code {err.returncode}\n\tstdout: {err.stdout}\n\tstderr: {err.stderr}\n=========')
        if err.returncode != -9:
            raise

        print("install_name_tool was killed. Retrying with x86_64 architecture...")
        install_name_tool = ["arch", "-arch", "x86_64"] + install_name_tool
        try:
            subprocess.run(install_name_tool, capture_output=True, check=True, text=True)
        except subprocess.CalledProcessError as inner_err:
            print(f'=========\ninstall_name_tool failed with code {inner_err.returncode}\n\tstdout: {inner_err.stdout}\n\tstderr: {inner_err.stderr}\n=========')
            raise


if __name__ == '__main__':
    parser = argparse.ArgumentParser()
    parser.add_argument('bundle', help='The bundle to fix')
    parser.add_argument('-config', help='Configuration type')
    args = parser.parse_args()

    if (args.config == "Debug"):
       exit(0)

    bundle = args.bundle

    if not os.path.exists(bundle):
        print('Bundle not found: ' + bundle)
        exit(1)

    if not os.path.isdir(bundle):
        run_install_name_tool(bundle, '@loader_path/../Frameworks')

        if bundle.find('modules') != -1:
            exit(0)

        bundle = os.path.join(os.path.dirname(args.bundle), '..', '..')
        bundle = os.path.abspath(bundle)

    print(f"Fixing bundle {bundle}")

    frameworks_dir = os.path.join(bundle, 'Contents', 'Frameworks')

    if not os.path.exists(frameworks_dir):
        print('Bundle does not contain a Frameworks directory: ' + bundle)
        exit(1)

    for file in os.listdir(frameworks_dir):
        file_path = os.path.join(frameworks_dir, file)
        if os.path.isfile(file_path):
            run_install_name_tool(file_path, '@loader_path')