def modify_object_macholib(cur_path, paths_to_paths): """ This function is used when install machO buildcaches on linux by rewriting mach-o loader commands for dependency library paths of mach-o binaries and the id path for mach-o libraries. Rewritting of rpaths is handled by replace_prefix_bin. Inputs mach-o binary to be modified dictionary mapping paths in old install layout to new install layout """ dll = MachO(cur_path) changedict = paths_to_paths def changefunc(path): npath = changedict.get(path, None) return npath dll.rewriteLoadCommands(changefunc) try: f = open(dll.filename, 'rb+') for header in dll.headers: f.seek(0) dll.write(f) f.seek(0, 2) f.flush() f.close() except Exception: pass return
def rewriteFramework(framework, frameworkMap): basename = os.path.splitext(os.path.basename(framework))[0] dyld = os.path.abspath(os.path.join(framework, basename)) macho = MachO(dyld) def changefunc(key): if key == dyld: return dyld dirname, filename = os.path.split(key) return frameworkMap.get(filename) macho.rewriteLoadCommands(changefunc) macho.write(open(dyld, "rb+"))
def rewriteFramework(framework, frameworkMap): basename = os.path.splitext(os.path.basename(framework))[0] dyld = os.path.abspath(os.path.join(framework, basename)) macho = MachO(dyld) def changefunc(key): if key == dyld: return dyld dirname, filename = os.path.split(key) return frameworkMap.get(filename) macho.rewriteLoadCommands(changefunc) macho.write(open(dyld, "rb+"))
def modify_object_macholib(cur_path, old_dir, new_dir): """ Modify MachO binary path_name by replacing old_dir with new_dir or the relative path to spack install root. The old install dir in LC_ID_DYLIB is replaced with the new install dir using py-macholib The old install dir in LC_LOAD_DYLIB is replaced with the new install dir using py-macholib The old install dir in LC_RPATH is replaced with the new install dir using using py-macholib """ if cur_path.endswith('.o'): return try: from macholib.MachO import MachO except ImportError as e: raise MissingMacholibException(e) def match_func(cpath): rpath = cpath.replace(old_dir, new_dir) return rpath dll = MachO(cur_path) dll.rewriteLoadCommands(match_func) try: f = open(dll.filename, 'rb+') for header in dll.headers: f.seek(0) dll.write(f) f.seek(0, 2) f.flush() f.close() except Exception: pass return
def mac_set_relative_dylib_deps(libname, distname): """ On Mac OS X set relative paths to dynamic library dependencies of `libname`. Relative paths allow to avoid using environment variable DYLD_LIBRARY_PATH. There are known some issues with DYLD_LIBRARY_PATH. Relative paths is more flexible mechanism. Current location of dependend libraries is derived from the location of the library path (paths start with '@loader_path'). 'distname' path of the library relative to dist directory of frozen executable. We need this to determine the level of directory level for @loader_path of binaries not found in dist directory. E.g. qt4 plugins are not in the same directory as Qt*.dylib files. Without using '@loader_path/../..' for qt plugins Mac OS X would not be able to resolve shared library dependencies and qt plugins will not be loaded. """ from macholib import util from macholib.MachO import MachO # Ignore bootloader otherwise PyInstaller fails with exception like # 'ValueError: total_size > low_offset (288 > 0)' if os.path.basename(libname) in _BOOTLOADER_FNAMES: return # Determine how many directories up is the directory with shared # dynamic libraries. '../' # E.g. ./qt4_plugins/images/ -> ./../../ parent_dir = '' # Check if distname is not only base filename. if os.path.dirname(distname): parent_level = len(os.path.dirname(distname).split(os.sep)) parent_dir = parent_level * (os.pardir + os.sep) def match_func(pth): """ For system libraries is still used absolute path. It is unchanged. """ # Leave system dynamic libraries unchanged if util.in_system_path(pth): return None # The older python.org builds that use system Tcl/Tk framework # have their _tkinter.cpython-*-darwin.so library linked against # /Library/Frameworks/Tcl.framework/Versions/8.5/Tcl and # /Library/Frameworks/Tk.framework/Versions/8.5/Tk, although the # actual frameworks are located in /System/Library/Frameworks. # Therefore, they slip through the above in_system_path() check, # and we need to exempt them manually. _exemptions = [ '/Library/Frameworks/Tcl.framework/', '/Library/Frameworks/Tk.framework/' ] if any([x in pth for x in _exemptions]): return None # Use relative path to dependent dynamic libraries based on the # location of the executable. return os.path.join('@loader_path', parent_dir, os.path.basename(pth)) # Rewrite mach headers with @loader_path. dll = MachO(libname) dll.rewriteLoadCommands(match_func) # Write changes into file. # Write code is based on macholib example. try: with open(dll.filename, 'rb+') as f: for header in dll.headers: f.seek(0) dll.write(f) f.seek(0, 2) f.flush() except Exception: pass
def mac_set_relative_dylib_deps(libname, distname): """ On Mac OS X set relative paths to dynamic library dependencies of `libname`. Relative paths allow to avoid using environment variable DYLD_LIBRARY_PATH. There are known some issues with DYLD_LIBRARY_PATH. Relative paths is more flexible mechanism. Current location of dependend libraries is derived from the location of the library path (paths start with '@loader_path'). 'distname' path of the library relative to dist directory of frozen executable. We need this to determine the level of directory level for @loader_path of binaries not found in dist directory. E.g. qt4 plugins are not in the same directory as Qt*.dylib files. Without using '@loader_path/../..' for qt plugins Mac OS X would not be able to resolve shared library dependencies and qt plugins will not be loaded. """ from macholib import util from macholib.MachO import MachO # Ignore bootloader otherwise PyInstaller fails with exception like # 'ValueError: total_size > low_offset (288 > 0)' if os.path.basename(libname) in _BOOTLOADER_FNAMES: return # Determine how many directories up is the directory with shared # dynamic libraries. '../' # E.g. ./qt4_plugins/images/ -> ./../../ parent_dir = '' # Check if distname is not only base filename. if os.path.dirname(distname): parent_level = len(os.path.dirname(distname).split(os.sep)) parent_dir = parent_level * (os.pardir + os.sep) def match_func(pth): """ For system libraries is still used absolute path. It is unchanged. """ # Match non system dynamic libraries. if not util.in_system_path(pth): # Use relative path to dependend dynamic libraries bases on # location of the executable. return os.path.join('@loader_path', parent_dir, os.path.basename(pth)) # Rewrite mach headers with @loader_path. dll = MachO(libname) dll.rewriteLoadCommands(match_func) # Write changes into file. # Write code is based on macholib example. try: with open(dll.filename, 'rb+') as f: for header in dll.headers: f.seek(0) dll.write(f) f.seek(0, 2) f.flush() except Exception: pass