Exemple #1
0
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)
Exemple #2
0
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)
Exemple #3
0
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)
Exemple #4
0
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
Exemple #5
0
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
Exemple #6
0
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
Exemple #7
0
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)
Exemple #8
0
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
Exemple #9
0
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)
Exemple #10
0
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)
Exemple #11
0
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)