You've already forked gnatstudio
mirror of
https://github.com/AdaCore/gnatstudio.git
synced 2026-02-12 12:42:33 -08:00
160 lines
5.4 KiB
Python
160 lines
5.4 KiB
Python
import os
|
|
import os.path
|
|
import re
|
|
import subprocess
|
|
import sys
|
|
|
|
OTOOL_LINE_FILTER = re.compile("(.*\.dylib\.?.*)\s\(compatibility.*$")
|
|
|
|
|
|
class Resolver(object):
|
|
"""Class used to track and resolve an executable's dependencies"""
|
|
|
|
inputs = []
|
|
to_copy = {}
|
|
ignore = []
|
|
to_analyze = []
|
|
|
|
def __init__(self, prefix, mains, library_path):
|
|
"""Takes a list of libraries or executable as input, and creates self.to_copy
|
|
containing the dependencies"""
|
|
self.prefix = prefix
|
|
self.library_path = library_path
|
|
self._resolve_library_dependencies(mains)
|
|
|
|
@staticmethod
|
|
def filterlines(lines):
|
|
"""Filters lines with regexp and returns the group 1 from matching lines"""
|
|
for line in lines:
|
|
match = OTOOL_LINE_FILTER.match(line)
|
|
if match:
|
|
yield match.group(1).strip().split()[0]
|
|
|
|
def prefix_filter(self, line):
|
|
if line in self.ignore:
|
|
return False
|
|
if line in list(self.to_copy.keys()):
|
|
return True
|
|
|
|
if not os.path.isabs(line):
|
|
return False
|
|
|
|
for prefix in self.library_path:
|
|
if line.startswith(prefix):
|
|
return True
|
|
|
|
if (
|
|
not line.startswith("/usr/lib")
|
|
and not line.startswith("/System/Library")
|
|
and not line.strip().split()[0].endswith("libgcc_s.1.dylib")
|
|
):
|
|
basename = os.path.basename(line)
|
|
for prefix in self.library_path:
|
|
tentative = os.path.join(prefix, "lib", basename)
|
|
if os.path.isfile(tentative):
|
|
print(
|
|
"Warning, using %s instead of %s: may have been moved"
|
|
% (tentative, line)
|
|
)
|
|
return True
|
|
# Ignore the library from the OS, and raise a warning for the others
|
|
print("!!! ERROR, library not available in any prefix: %s" % line)
|
|
self.ignore.append(line)
|
|
|
|
return False
|
|
|
|
def to_bundle(self, filename, prefix):
|
|
"""Returns the bundle's filename for library filename"""
|
|
return filename.replace(prefix, self.prefix)
|
|
|
|
def _get_deps(self, filename):
|
|
"""Retrieve the list of filename's dynamic library in use"""
|
|
|
|
cmd = "xcrun otool -L %s" % filename
|
|
with os.popen(cmd) as f:
|
|
libs = Resolver.filterlines([line.strip() for line in f])
|
|
libs = list(filter(self.prefix_filter, libs))
|
|
return set(libs)
|
|
|
|
def _add_deps(self, f_list):
|
|
"""Filters libraries based on their location"""
|
|
|
|
for lib in f_list:
|
|
libs = self._get_deps(lib)
|
|
for lib in libs:
|
|
if lib in list(self.to_copy.keys()):
|
|
continue
|
|
|
|
if lib in self.ignore:
|
|
continue
|
|
|
|
found = False
|
|
for prefix in self.library_path:
|
|
if lib.startswith(prefix):
|
|
# From authorized prefixes
|
|
self.to_copy[lib] = (lib, self.to_bundle(lib, prefix))
|
|
self.inputs.append(lib)
|
|
found = True
|
|
break
|
|
if found:
|
|
continue
|
|
|
|
# lib not found in prefixes, so it's been moved at some point
|
|
basename = os.path.basename(lib)
|
|
found = False
|
|
for prefix in self.library_path:
|
|
tentative = os.path.join(prefix, "lib", basename)
|
|
if os.path.isfile(tentative):
|
|
self.to_copy[lib] = (
|
|
tentative,
|
|
self.to_bundle(tentative, prefix),
|
|
)
|
|
self.inputs.append(tentative)
|
|
found = True
|
|
break
|
|
|
|
def to_relative(self, filename):
|
|
"""Rebase the library dependencies relative to filename location"""
|
|
|
|
lines = self._get_deps(filename)
|
|
|
|
for lib in lines:
|
|
libname = os.path.basename(lib)
|
|
cmd = "xcrun install_name_tool -change %s %s %s" % (lib, libname, filename)
|
|
subprocess.call(cmd, shell=True)
|
|
cmd = "xcrun install_name_tool -id %s %s" % (
|
|
os.path.basename(filename),
|
|
filename,
|
|
)
|
|
subprocess.call(cmd, shell=True)
|
|
|
|
def _resolve_library_dependencies(self, mains):
|
|
"""Recursively gather all library dependencies of executable mainpath"""
|
|
|
|
self.inputs = mains[:]
|
|
n_iterations = 0
|
|
n_paths = -1
|
|
|
|
for main in mains:
|
|
do_copy = False
|
|
# First check if we need to copy the files given as input
|
|
for prefix in self.library_path:
|
|
if main.startswith(prefix):
|
|
self.to_copy[main] = (main, main.replace(prefix, self.prefix))
|
|
do_copy = True
|
|
break
|
|
if not do_copy:
|
|
self.to_copy[main] = ("", main)
|
|
|
|
# we iterate until we don't have any new library
|
|
while n_paths != len(self.to_copy):
|
|
n_paths = len(self.to_copy)
|
|
|
|
# run otool on the input files
|
|
self._add_deps(self.inputs[:])
|
|
|
|
n_iterations += 1
|
|
if n_iterations > 10:
|
|
print("Too many tries to resolve library dependencies")
|
|
sys.exit(1)
|