1#!/usr/bin/env python3 2import os 3import sys 4import shutil 5import subprocess 6 7strip = False 8rpath = False 9no_copy = False 10# steam_runtime = os.getenv("STEAM_RUNTIME", "") 11steam_runtime = os.getenv("STEAMOS", "") 12 13 14def fix_binary(path): 15 changes = 0 16 if os.path.exists(path + ".standalone"): 17 return changes 18 if not os.path.exists(path): 19 raise Exception("could not find " + repr(path)) 20 21 # find library locations 22 args = ["ldd", path] 23 p = subprocess.Popen(args, stdout=subprocess.PIPE) 24 # noinspection PyUnresolvedReferences 25 data = p.stdout.read().decode("UTF-8") 26 if p.wait() != 0: 27 return 0 28 print("fixing", path, "no_copy =", no_copy) 29 library_locations = {} 30 for line in data.split("\n"): 31 line = line.strip() 32 if "=>" not in line: 33 continue 34 library_locations[line.split(" ")[0]] = line.split(" ")[2] 35 36 # find direct dependencies 37 args = ["objdump", "-p", path] 38 p = subprocess.Popen(args, stdout=subprocess.PIPE) 39 # noinspection PyUnresolvedReferences 40 data = p.stdout.read().decode("UTF-8") 41 if p.wait() != 0: 42 return 0 43 for line in data.split("\n"): 44 line = line.strip() 45 # print(line) 46 if not line.startswith("NEEDED"): 47 continue 48 print(line) 49 library = line.split(" ")[-1] 50 print(library) 51 if ignore_library(library): 52 continue 53 library_source = library_locations[library] 54 library_source = os.path.normpath(library_source) 55 print(library, library_source) 56 # if steam_runtime and library_source.startswith(steam_runtime): 57 # if steam_runtime and not library_source.startswith("/usr/local"): 58 if steam_runtime and not library_source.startswith("/home"): 59 print("skipping steam runtime library") 60 continue 61 if no_copy: 62 print("no_copy is set") 63 continue 64 dst = os.path.join(os.path.dirname(path), library) 65 if not os.path.exists(dst): 66 print("copying", library) 67 shutil.copy(library_source, dst) 68 os.chmod(dst, 0o644) 69 changes += 1 70 if strip: 71 # strip does not work after patchelf has been run 72 command = "strip '{}'".format(path) 73 print(command) 74 os.system(command) 75 if rpath: 76 command = "patchelf --set-rpath '{}' '{}'".format(rpath, path) 77 print(command) 78 assert os.system(command) == 0 79 # to make sure strip is not run again 80 os.system("touch '{}.standalone'".format(path)) 81 return changes 82 83 84def ignore_library(name): 85 if name.startswith("libgpg-error.so"): 86 raise Exception( 87 "Bundling libgpg-error (libgcrypt?) breaks Intel GL driver") 88 89 if name.startswith("linux-gate.so"): 90 return True 91 if name.startswith("linux-vdso.so"): 92 return True 93 if name.startswith("ld-linux.so.2"): 94 return True 95 if name.startswith("ld-linux-x86-64.so"): 96 return True 97 98 if name.startswith("libc.so"): 99 return True 100 if name.startswith("libstdc++.so"): 101 # Including libstdc++.sp breaks libGL loading with Intel on Ubuntu 16.10 102 # libGL error: unable to load driver: i965_dri.so 103 return True 104 if name.startswith("libgcc_s.so"): 105 # Might as well skip this one also, to avoid potential similar problems. 106 return True 107 if name.startswith("libpthread.so"): 108 return True 109 if name.startswith("libm.so"): 110 return True 111 if name.startswith("libdl.so"): 112 return True 113 if name.startswith("libresolv.so"): 114 return True 115 if name.startswith("librt.so"): 116 return True 117 if name.startswith("libutil.so"): 118 return True 119 # if name.startswith("libpcre.so"): 120 # # Problem with OpenAL on Ubuntu 16.04 if this is included. 121 # return True 122 123 if name.startswith("libGL.so"): 124 return True 125 if name.startswith("libGLU.so"): 126 return True 127 if name.startswith("libEGL.so"): 128 return True 129 130 if name.startswith("libasound.so"): 131 # Alsa library is in LSB, looks like only "old" interfaces are used 132 # by SDL2. 133 return True 134 135 if name.startswith("libX11.so"): 136 return True 137 if name.startswith("libXext.so"): 138 return True 139 if name.startswith("libXcursor.so"): 140 return True 141 if name.startswith("libXinerama.so"): 142 return True 143 if name.startswith("libXi.so"): 144 return True 145 if name.startswith("libXrandr.so"): 146 return True 147 if name.startswith("libXss.so"): 148 return True 149 if name.startswith("libXxf86vm.so"): 150 return True 151 # if name.startswith("libxkbcommon.so"): 152 # return True 153 if name.startswith("libxcb.so"): 154 return True 155 156 return False 157 158 159def fix_iteration(app): 160 binaries = [] 161 binaries_dir = app 162 for name in os.listdir(binaries_dir): 163 binaries.append(os.path.join(binaries_dir, name)) 164 changes = 0 165 for binary in binaries: 166 changes += fix_binary(binary) 167 return changes 168 169 170def main(): 171 global no_copy, strip, rpath 172 for arg in list(sys.argv): 173 if arg.startswith("--rpath="): 174 sys.argv.remove(arg) 175 rpath = arg[8:] 176 elif arg == "--no-copy": 177 sys.argv.remove("--no-copy") 178 no_copy = True 179 elif arg == "--strip": 180 sys.argv.remove("--strip") 181 strip = True 182 app = sys.argv[1] 183 while True: 184 changes = fix_iteration(app) 185 if changes == 0: 186 break 187 for name in os.listdir(app): 188 if name.endswith(".standalone"): 189 os.remove(os.path.join(app, name)) 190 191 192if __name__ == "__main__": 193 main() 194