def _show_linking_messages(files, errors, needed_dsos_for_file, build_prefix, run_prefix, pkg_name, error_overlinking, runpath_whitelist, verbose, requirements_run, lib_packages, lib_packages_used, whitelist, sysroots, sysroot_prefix, sysroot_substitution): for f in files: path = os.path.join(run_prefix, f) if not codefile_type(path): continue warn_prelude = "WARNING ({},{})".format(pkg_name, f) err_prelude = " ERROR ({},{})".format(pkg_name, f) info_prelude = " INFO ({},{})".format(pkg_name, f) msg_prelude = err_prelude if error_overlinking else warn_prelude try: runpaths = get_runpaths(path) except: _print_msg(errors, '{}: pyldd.py failed to process'.format(warn_prelude)) continue if runpaths and not (runpath_whitelist or any(fnmatch.fnmatch(f, w) for w in runpath_whitelist)): _print_msg(errors, '{}: runpaths {} found in {}'.format(msg_prelude, runpaths, path), verbose=verbose) needed = needed_dsos_for_file[f] # imps = get_imports_memoized(path, None) for needed_dso in needed: needed_dso = needed_dso.replace('/', os.sep) if not needed_dso.startswith(os.sep) and not needed_dso.startswith('$'): _lookup_in_prefix_packages(errors, needed_dso, files, run_prefix, whitelist, info_prelude, msg_prelude, warn_prelude, verbose, requirements_run, lib_packages, lib_packages_used) elif needed_dso.startswith(build_prefix): _print_msg(errors, "{}: {} found in build prefix; should never happen".format( err_prelude, needed_dso), verbose=verbose) else: _lookup_in_system_whitelists(errors, whitelist, needed_dso, sysroots, msg_prelude, info_prelude, sysroot_prefix, sysroot_substitution, verbose)
def fix_shebang(f, prefix, build_python, osx_is_app=False): path = os.path.join(prefix, f) if codefile_type(path): return elif os.path.islink(path): return elif not os.path.isfile(path): return if os.stat(path).st_size == 0: return bytes_ = False with io.open(path, encoding=locale.getpreferredencoding(), mode='r+') as fi: try: data = fi.read(100) fi.seek(0) except UnicodeDecodeError: # file is binary return SHEBANG_PAT = re.compile(r'^#!.+$', re.M) # regexp on the memory mapped file so we only read it into # memory if the regexp matches. try: mm = utils.mmap_mmap(fi.fileno(), 0, tagname=None, flags=utils.mmap_MAP_PRIVATE) except OSError: mm = fi.read() try: m = SHEBANG_PAT.match(mm) except TypeError: SHEBANG_PAT = re.compile(br'^#!.+$', re.M) bytes_ = True m = SHEBANG_PAT.match(mm) python_str = b'python' if bytes_ else 'python' if not (m and python_str in m.group()): return data = mm[:] py_exec = '#!' + ('/bin/bash ' + prefix + '/bin/pythonw' if sys.platform == 'darwin' and osx_is_app else prefix + '/bin/' + os.path.basename(build_python)) if bytes_ and hasattr(py_exec, 'encode'): py_exec = py_exec.encode() new_data = SHEBANG_PAT.sub(py_exec, data, count=1) if new_data == data: return print("updating shebang:", f) with io.open(path, 'w', encoding=locale.getpreferredencoding()) as fo: try: fo.write(new_data) except TypeError: fo.write(new_data.decode()) os.chmod(path, 0o775)
def post_process_shared_lib(m, f, files): path = os.path.join(m.config.host_prefix, f) codefile_t = codefile_type(path) if not codefile_t: return if sys.platform.startswith('linux') and codefile_t == 'elffile': mk_relative_linux(f, m.config.host_prefix, rpaths=m.get_value('build/rpaths', ['lib'])) elif sys.platform == 'darwin' and codefile_t == 'machofile': mk_relative_osx(path, m.config.host_prefix, m.config.build_prefix, files=files)
def osx_ch_link(path, link_dict, host_prefix, build_prefix, files): link = link_dict['name'] print("Fixing linking of %s in %s" % (link, path)) if build_prefix != host_prefix and link.startswith(build_prefix): link = link.replace(build_prefix, host_prefix) print( ".. seems to be linking to a compiler runtime, replacing build prefix with " "host prefix and") if not codefile_type(link): sys.exit( "Error: Compiler runtime library in build prefix not found in host prefix %s" % link) else: print(".. fixing linking of %s in %s instead" % (link, path)) link_loc = find_lib(link, host_prefix, files, path) print("New link location is %s" % (link_loc)) if not link_loc: return lib_to_link = os.path.relpath(os.path.dirname(link_loc), 'lib') # path_to_lib = utils.relative(path[len(prefix) + 1:]) # e.g., if # path = '/build_prefix/lib/some/stuff/libstuff.dylib' # link_loc = 'lib/things/libthings.dylib' # then # lib_to_link = 'things' # path_to_lib = '../..' # @rpath always means 'lib', link will be at # @rpath/lib_to_link/basename(link), like @rpath/things/libthings.dylib. # For when we can't use @rpath, @loader_path means the path to the library # ('path'), so from path to link is # @loader_path/path_to_lib/lib_to_link/basename(link), like # @loader_path/../../things/libthings.dylib. ret = '@rpath/%s/%s' % (lib_to_link, os.path.basename(link)) # XXX: IF the above fails for whatever reason, the below can be used # TODO: This might contain redundant ..'s if link and path are both in # some subdirectory of lib. # ret = '@loader_path/%s/%s/%s' % (path_to_lib, lib_to_link, basename(link)) ret = ret.replace('/./', '/') return ret
def _map_file_to_package(files, run_prefix, build_prefix, all_needed_dsos, pkg_vendored_dist, ignore_list_syms, sysroot_substitution): # Form a mapping of file => package prefix_owners = {} contains_dsos = {} contains_static_libs = {} # Used for both dsos and static_libs all_lib_exports = {} for prefix in (run_prefix, build_prefix): for subdir2, _, filez in os.walk(prefix): for file in filez: fp = os.path.join(subdir2, file) dynamic_lib = any(glob2.fnmatch.fnmatch(fp, ext) for ext in ('*.so*', '*.dylib*', '*.dll')) and \ codefile_type(fp, skip_symlinks=False) is not None static_lib = any(glob2.fnmatch.fnmatch(fp, ext) for ext in ('*.a', '*.lib')) # Looking at all the files is very slow. if not dynamic_lib and not static_lib: continue rp = os.path.relpath(fp, prefix) if dynamic_lib and rp not in all_needed_dsos: continue if rp in all_lib_exports: continue owners = prefix_owners[rp] if rp in prefix_owners else [] # Self-vendoring, not such a big deal but may as well report it? if not len(owners): if rp in files: owners.append(pkg_vendored_dist) new_pkgs = list(which_package(rp, prefix)) # Cannot filter here as this means the DSO (eg libomp.dylib) will not be found in any package # [owners.append(new_pkg) for new_pkg in new_pkgs if new_pkg not in owners # and not any([glob2.fnmatch.fnmatch(new_pkg.name, i) for i in ignore_for_statics])] for new_pkg in new_pkgs: if new_pkg not in owners: owners.append(new_pkg) prefix_owners[rp] = owners if len(prefix_owners[rp]): exports = set(e for e in get_exports_memoized(fp) if not any(glob2.fnmatch.fnmatch(e, pattern) for pattern in ignore_list_syms)) all_lib_exports[rp] = exports # Check codefile_type to filter out linker scripts. if dynamic_lib: contains_dsos[prefix_owners[rp][0]] = True elif static_lib: if sysroot_substitution in fp: if (prefix_owners[rp][0].name.startswith('gcc_impl_linux') or prefix_owners[rp][0].name == 'llvm'): continue print("sysroot in {}, owner is {}".format(fp, prefix_owners[rp][0])) contains_static_libs[prefix_owners[rp][0]] = True return prefix_owners, contains_dsos, contains_static_libs, all_lib_exports
def osx_ch_link(path, link_dict, host_prefix, build_prefix, files): link = link_dict['name'] print("Fixing linking of %s in %s" % (link, path)) if build_prefix != host_prefix and link.startswith(build_prefix): link = link.replace(build_prefix, host_prefix) print(".. seems to be linking to a compiler runtime, replacing build prefix with " "host prefix and") if not codefile_type(link): sys.exit("Error: Compiler runtime library in build prefix not found in host prefix %s" % link) else: print(".. fixing linking of %s in %s instead" % (link, path)) link_loc = find_lib(link, host_prefix, files, path) print("New link location is %s" % (link_loc)) if not link_loc: return lib_to_link = os.path.relpath(os.path.dirname(link_loc), 'lib') # path_to_lib = utils.relative(path[len(prefix) + 1:]) # e.g., if # path = '/build_prefix/lib/some/stuff/libstuff.dylib' # link_loc = 'lib/things/libthings.dylib' # then # lib_to_link = 'things' # path_to_lib = '../..' # @rpath always means 'lib', link will be at # @rpath/lib_to_link/basename(link), like @rpath/things/libthings.dylib. # For when we can't use @rpath, @loader_path means the path to the library # ('path'), so from path to link is # @loader_path/path_to_lib/lib_to_link/basename(link), like # @loader_path/../../things/libthings.dylib. ret = '@rpath/%s/%s' % (lib_to_link, os.path.basename(link)) # XXX: IF the above fails for whatever reason, the below can be used # TODO: This might contain redundant ..'s if link and path are both in # some subdirectory of lib. # ret = '@loader_path/%s/%s/%s' % (path_to_lib, lib_to_link, basename(link)) ret = ret.replace('/./', '/') return ret
def check_symlinks(files, prefix, croot): if readlink is False: return # Not on Unix system msgs = [] real_build_prefix = os.path.realpath(prefix) for f in files: path = os.path.join(real_build_prefix, f) if os.path.islink(path): link_path = readlink(path) real_link_path = os.path.realpath(path) # symlinks to binaries outside of the same dir don't work. RPATH stuff gets confused # because ld.so follows symlinks in RPATHS # If condition exists, then copy the file rather than symlink it. if (not os.path.dirname(link_path) == os.path.dirname(real_link_path) and codefile_type(f)): os.remove(path) utils.copy_into(real_link_path, path) elif real_link_path.startswith(real_build_prefix): # If the path is in the build prefix, this is fine, but # the link needs to be relative relative_path = os.path.relpath(real_link_path, os.path.dirname(path)) if not link_path.startswith( '.') and link_path != relative_path: # Don't change the link structure if it is already a # relative link. It's possible that ..'s later in the path # can result in a broken link still, but we'll assume that # such crazy things don't happen. print("Making absolute symlink %s -> %s relative" % (f, link_path)) os.unlink(path) os.symlink(relative_path, path) else: # Symlinks to absolute paths on the system (like /usr) are fine. if real_link_path.startswith(croot): msgs.append("%s is a symlink to a path that may not " "exist after the build is completed (%s)" % (f, link_path)) if msgs: for msg in msgs: print("Error: %s" % msg, file=sys.stderr) sys.exit(1)
def _collect_needed_dsos(sysroots, files, run_prefix, sysroot_substitution, build_prefix, build_prefix_substitution): all_needed_dsos = set() needed_dsos_for_file = dict() sysroot = sysroots[0] if sysroots else '' for f in files: path = os.path.join(run_prefix, f) if not codefile_type(path): continue needed = get_linkages_memoized(path, resolve_filenames=True, recurse=False, sysroot=sysroot, envroot=run_prefix) if sysroot: needed = [n.replace(sysroot, sysroot_substitution) if n.startswith(sysroot) else n for n in needed] needed = [n.replace(build_prefix, build_prefix_substitution) if n.startswith(build_prefix) else n for n in needed] needed = [os.path.relpath(n, run_prefix) if n.startswith(run_prefix) else n for n in needed] needed_dsos_for_file[f] = needed all_needed_dsos = all_needed_dsos.union(needed) all_needed_dsos.add(f) return all_needed_dsos, needed_dsos_for_file
def check_symlinks(files, prefix, croot): if readlink is False: return # Not on Unix system msgs = [] real_build_prefix = os.path.realpath(prefix) for f in files: path = os.path.join(real_build_prefix, f) if os.path.islink(path): link_path = readlink(path) real_link_path = os.path.realpath(path) # symlinks to binaries outside of the same dir don't work. RPATH stuff gets confused # because ld.so follows symlinks in RPATHS # If condition exists, then copy the file rather than symlink it. if (not os.path.dirname(link_path) == os.path.dirname(real_link_path) and codefile_type(f)): os.remove(path) utils.copy_into(real_link_path, path) elif real_link_path.startswith(real_build_prefix): # If the path is in the build prefix, this is fine, but # the link needs to be relative relative_path = os.path.relpath(real_link_path, os.path.dirname(path)) if not link_path.startswith('.') and link_path != relative_path: # Don't change the link structure if it is already a # relative link. It's possible that ..'s later in the path # can result in a broken link still, but we'll assume that # such crazy things don't happen. print("Making absolute symlink %s -> %s relative" % (f, link_path)) os.unlink(path) os.symlink(relative_path, path) else: # Symlinks to absolute paths on the system (like /usr) are fine. if real_link_path.startswith(croot): msgs.append("%s is a symlink to a path that may not " "exist after the build is completed (%s)" % (f, link_path)) if msgs: for msg in msgs: print("Error: %s" % msg, file=sys.stderr) sys.exit(1)
def check_overlinking(m, files): pkg_name = m.get_value('package/name') errors = [] run_reqs = [req.split(' ')[0] for req in m.meta.get('requirements', {}).get('run', [])] # sysroots and whitelists are similar, but the subtle distinctions are important. sysroots = glob(os.path.join(m.config.build_prefix, '**', 'sysroot')) whitelist = [] if 'target_platform' in m.config.variant and m.config.variant['target_platform'] == 'osx-64': if not len(sysroots): sysroots = ['/usr/lib', '/opt/X11', '/System/Library/Frameworks'] whitelist = ['/opt/X11/', '/usr/lib/libSystem.B.dylib', '/usr/lib/libcrypto.0.9.8.dylib', '/usr/lib/libobjc.A.dylib', '/System/Library/Frameworks/Accelerate.framework/*', '/System/Library/Frameworks/AGL.framework/*', '/System/Library/Frameworks/AppKit.framework/*', '/System/Library/Frameworks/ApplicationServices.framework/*', '/System/Library/Frameworks/AudioToolbox.framework/*', '/System/Library/Frameworks/AudioUnit.framework/*', '/System/Library/Frameworks/AVFoundation.framework/*', '/System/Library/Frameworks/CFNetwork.framework/*', '/System/Library/Frameworks/Carbon.framework/*', '/System/Library/Frameworks/Cocoa.framework/*', '/System/Library/Frameworks/CoreAudio.framework/*', '/System/Library/Frameworks/CoreFoundation.framework/*', '/System/Library/Frameworks/CoreGraphics.framework/*', '/System/Library/Frameworks/CoreMedia.framework/*', '/System/Library/Frameworks/CoreBluetooth.framework/*', '/System/Library/Frameworks/CoreMIDI.framework/*', '/System/Library/Frameworks/CoreMedia.framework/*', '/System/Library/Frameworks/CoreServices.framework/*', '/System/Library/Frameworks/CoreText.framework/*', '/System/Library/Frameworks/CoreVideo.framework/*', '/System/Library/Frameworks/CoreWLAN.framework/*', '/System/Library/Frameworks/DiskArbitration.framework/*', '/System/Library/Frameworks/Foundation.framework/*', '/System/Library/Frameworks/GameController.framework/*', '/System/Library/Frameworks/GLKit.framework/*', '/System/Library/Frameworks/ImageIO.framework/*', '/System/Library/Frameworks/IOBluetooth.framework/*', '/System/Library/Frameworks/IOKit.framework/*', '/System/Library/Frameworks/IOSurface.framework/*', '/System/Library/Frameworks/OpenAL.framework/*', '/System/Library/Frameworks/OpenGL.framework/*', '/System/Library/Frameworks/Quartz.framework/*', '/System/Library/Frameworks/QuartzCore.framework/*', '/System/Library/Frameworks/Security.framework/*', '/System/Library/Frameworks/StoreKit.framework/*', '/System/Library/Frameworks/SystemConfiguration.framework/*', '/System/Library/Frameworks/WebKit.framework/*'] whitelist += m.meta.get('build', {}).get('missing_dso_whitelist', []) for f in files: path = os.path.join(m.config.host_prefix, f) if not codefile_type(path): continue warn_prelude = "WARNING ({},{})".format(pkg_name, f) err_prelude = " ERROR ({},{})".format(pkg_name, f) info_prelude = " INFO ({},{})".format(pkg_name, f) msg_prelude = err_prelude if m.config.error_overlinking else warn_prelude runpaths = get_runpaths(path) if len(runpaths): print_msg(errors, '{}: runpaths {} found in {}'.format(msg_prelude, runpaths, path)) needed = inspect_linkages(path, resolve_filenames=True, recurse=False) for needed_dso in needed: if needed_dso.startswith(m.config.host_prefix): in_prefix_dso = os.path.normpath(needed_dso.replace(m.config.host_prefix + '/', '')) n_dso_p = "Needed DSO {}".format(in_prefix_dso) and_also = " (and also in this package)" if in_prefix_dso in files else "" pkgs = list(which_package(in_prefix_dso, m.config.host_prefix)) in_pkgs_in_run_reqs = [pkg for pkg in pkgs if pkg.quad[0] in run_reqs] in_whitelist = any([glob2.fnmatch.fnmatch(in_prefix_dso, w) for w in whitelist]) if in_whitelist: print_msg(errors, '{}: {} found in the whitelist'. format(info_prelude, n_dso_p)) elif len(in_pkgs_in_run_reqs) == 1 and m.config.verbose: print_msg(errors, '{}: {} found in {}{}'.format(info_prelude, n_dso_p, in_pkgs_in_run_reqs[0], and_also)) elif len(in_pkgs_in_run_reqs) == 0 and len(pkgs) > 0: print_msg(errors, '{}: {} found in {}{}'.format(msg_prelude, n_dso_p, [p.quad[0] for p in pkgs], and_also)) print_msg(errors, '{}: .. but {} not in reqs/run, i.e. it is overlinked' ' (likely) or a missing dependency (less likely)'. format(msg_prelude, [p.quad[0] for p in pkgs])) elif len(in_pkgs_in_run_reqs) > 1: print_msg(errors, '{}: {} found in multiple packages in run/reqs: {}{}' .format(warn_prelude, in_prefix_dso, [p.quad[0] for p in in_pkgs_in_run_reqs], and_also)) else: if in_prefix_dso not in files: print_msg(errors, '{}: {} not found in any packages'.format(msg_prelude, in_prefix_dso)) elif m.config.verbose: print_msg(errors, '{}: {} found in this package'.format(info_prelude, in_prefix_dso)) elif needed_dso.startswith(m.config.build_prefix): print_msg(errors, "ERROR: {} found in build prefix; should never happen".format( needed_dso)) else: # A system or ignored dependency. We should be able to find it in one of the CDT o # compiler packages on linux or at in a sysroot folder on other OSes. These usually # start with '$RPATH/' which indicates pyldd did not find them, so remove that now. if needed_dso.startswith('$RPATH/'): needed_dso = needed_dso.replace('$RPATH/', '') in_whitelist = any([glob2.fnmatch.fnmatch(needed_dso, w) for w in whitelist]) if in_whitelist: n_dso_p = "Needed DSO {}".format(needed_dso) print_msg(errors, '{}: {} found in the whitelist'. format(info_prelude, n_dso_p)) elif m.config.verbose and len(sysroots): # Check id we have a CDT package. dso_fname = os.path.basename(needed_dso) sysroot_files = [] for sysroot in sysroots: sysroot_files.extend(glob(os.path.join(sysroot, '**', dso_fname))) if len(sysroot_files): # Removing config.build_prefix is only *really* for Linux, though we could # use CONDA_BUILD_SYSROOT for macOS. We should figure out what to do about # /opt/X11 too. # Find the longest suffix match. rev_needed_dso = needed_dso[::-1] match_lens = [len(os.path.commonprefix([s[::-1], rev_needed_dso])) for s in sysroot_files] idx = max(range(len(match_lens)), key=match_lens.__getitem__) in_prefix_dso = os.path.normpath(sysroot_files[idx].replace( m.config.build_prefix + '/', '')) n_dso_p = "Needed DSO {}".format(in_prefix_dso) pkgs = list(which_package(in_prefix_dso, m.config.build_prefix)) if len(pkgs): print_msg(errors, '{}: {} found in CDT/compiler package {}'. format(info_prelude, n_dso_p, pkgs[0])) else: print_msg(errors, '{}: {} not found in any CDT/compiler package,' ' nor the whitelist?!'. format(msg_prelude, n_dso_p)) else: print_msg(errors, "{}: {} not found in sysroot, is this binary repackaging?" " .. do you need to use install_name_tool/patchelf?". format(msg_prelude, needed_dso)) else: print_msg(errors, "{}: did not find - or even know where to look for: {}". format(msg_prelude, needed_dso)) if len(errors): sys.exit(1)
def check_overlinking_impl(pkg_name, pkg_version, build_str, build_number, subdir, ignore_run_exports, requirements_run, requirements_build, requirements_host, run_prefix, build_prefix, missing_dso_whitelist, runpath_whitelist, error_overlinking, error_overdepending, verbose, exception_on_error, files, bldpkgs_dirs, output_folder, channel_urls): verbose = True errors = [] sysroot_substitution = '$SYSROOT/' build_prefix_substitution = '$PATH/' # Used to detect overlinking (finally) requirements_run = [req.split(' ')[0] for req in requirements_run] packages = dists_from_names(requirements_run, run_prefix) ignore_list = utils.ensure_list(ignore_run_exports) if subdir.startswith('linux'): ignore_list.append('libgcc-ng') package_nature = {package: library_nature(package, run_prefix, subdir, bldpkgs_dirs, output_folder, channel_urls) for package in packages} lib_packages = set([package for package in packages if package.quad[0] not in ignore_list and package_nature[package] != 'non-library']) # The last package of requirements_run is this package itself, add it as being used # incase it qualifies as a library package. if len(packages) and packages[-1] in lib_packages: lib_packages_used = set((packages[-1],)) else: lib_packages_used = set() pkg_vendored_dist = _get_fake_pkg_dist(pkg_name, pkg_version, build_str, build_number) ignore_list_syms = ['main', '_main', '*get_pc_thunk*', '___clang_call_terminate', '_timeout'] # ignore_for_statics = ['gcc_impl_linux*', 'compiler-rt*', 'llvm-openmp*', 'gfortran_osx*'] # sysroots and whitelists are similar, but the subtle distinctions are important. sysroot_prefix = build_prefix sysroots = [sysroot + os.sep for sysroot in glob(os.path.join(sysroot_prefix, '**', 'sysroot'))] whitelist = [] if not len(sysroots): if subdir == 'osx-64': sysroots = ['/usr/lib/', '/opt/X11/', '/System/Library/Frameworks/'] whitelist = DEFAULT_MAC_WHITELIST elif subdir.startswith('win'): sysroots = ['C:/Windows'] whitelist = DEFAULT_WIN_WHITELIST # LIEF is very slow at decoding some DSOs, so we only let it look at ones that we link to (and ones we # have built). all_needed_dsos, needed_dsos_for_file = _collect_needed_dsos(sysroots, files, run_prefix, sysroot_substitution, build_prefix, build_prefix_substitution) prefix_owners, _, _, all_lib_exports = _map_file_to_package( files, run_prefix, build_prefix, all_needed_dsos, pkg_vendored_dist, ignore_list_syms, sysroot_substitution) for f in files: path = os.path.join(run_prefix, f) filetype = codefile_type(path) if not filetype or filetype not in filetypes_for_platform[subdir.split('-')[0]]: continue needed = needed_dsos_for_file[f] for needed_dso in needed: if (error_overlinking and not needed_dso.startswith('/') and not needed_dso.startswith(sysroot_substitution) and not needed_dso.startswith(build_prefix_substitution) and needed_dso not in prefix_owners): print(" ERROR :: {} not in prefix_owners".format(needed_dso)) sys.exit(1) whitelist += missing_dso_whitelist or [] _show_linking_messages(files, errors, needed_dsos_for_file, build_prefix, run_prefix, pkg_name, error_overlinking, runpath_whitelist, verbose, requirements_run, lib_packages, lib_packages_used, whitelist, sysroots, sysroot_prefix, sysroot_substitution, subdir) if lib_packages_used != lib_packages: info_prelude = " INFO ({})".format(pkg_name) warn_prelude = "WARNING ({})".format(pkg_name) err_prelude = " ERROR ({})".format(pkg_name) for lib in lib_packages - lib_packages_used: if package_nature[lib] == 'run-exports library': msg_prelude = err_prelude if error_overdepending else warn_prelude elif package_nature[lib] == 'plugin library': msg_prelude = info_prelude else: msg_prelude = warn_prelude _print_msg(errors, "{}: {} package {} in requirements/run but it is not used " "(i.e. it is overdepending or perhaps statically linked? " "If that is what you want then add it to `build/ignore_run_exports`)" .format(msg_prelude, package_nature[lib], lib), verbose=verbose) if len(errors): if exception_on_error: overlinking_errors = [error for error in errors if "overlinking" in error] if len(overlinking_errors): raise OverLinkingError(overlinking_errors) overdepending_errors = [error for error in errors if "overdepending" in error] if len(overdepending_errors): raise OverDependingError(overdepending_errors) else: sys.exit(1)