169 lines · python
1#!/usr/bin/env python32 3#4# //===----------------------------------------------------------------------===//5# //6# // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.7# // See https://llvm.org/LICENSE.txt for license information.8# // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception9# //10# //===----------------------------------------------------------------------===//11#12 13import argparse14import os15import platform16import re17import sys18from libomputils import (19 ScriptError,20 error,21 execute_command,22 print_info_line,23 print_error_line,24)25 26 27def get_deps_readelf(filename):28 """Get list of dependencies from readelf"""29 deps = []30 # Force readelf call to be in English31 os.environ["LANG"] = "C"32 r = execute_command(["readelf", "-d", filename])33 if r.returncode != 0:34 error("readelf -d {} failed".format(filename))35 neededRegex = re.compile(r"\(NEEDED\)\s+Shared library: \[([a-zA-Z0-9_.-]+)\]")36 for line in r.stdout.split(os.linesep):37 match = neededRegex.search(line)38 if match:39 deps.append(match.group(1))40 return deps41 42 43def get_deps_otool(filename):44 """Get list of dependencies from otool"""45 deps = []46 r = execute_command(["otool", "-L", filename])47 if r.returncode != 0:48 error("otool -L {} failed".format(filename))49 libRegex = re.compile(r"([^ \t]+)\s+\(compatibility version ")50 thisLibRegex = re.compile(r"@rpath/{}".format(os.path.basename(filename)))51 for line in r.stdout.split(os.linesep):52 match = thisLibRegex.search(line)53 if match:54 # Don't include the library itself as a needed dependency55 continue56 match = libRegex.search(line)57 if match:58 deps.append(match.group(1))59 continue60 return deps61 62 63def get_deps_link(filename):64 """Get list of dependecies from link (Windows OS)"""65 depsSet = set([])66 f = filename.lower()67 args = ["link", "/DUMP"]68 if f.endswith(".lib"):69 args.append("/DIRECTIVES")70 elif f.endswith(".dll") or f.endswith(".exe"):71 args.append("/DEPENDENTS")72 else:73 error("unrecognized file extension: {}".format(filename))74 args.append(filename)75 r = execute_command(args)76 if r.returncode != 0:77 error("{} failed".format(args.command))78 if f.endswith(".lib"):79 regex = re.compile(r"\s*[-/]defaultlib:(.*)\s*$")80 for line in r.stdout.split(os.linesep):81 line = line.lower()82 match = regex.search(line)83 if match:84 depsSet.add(match.group(1))85 else:86 started = False87 markerStart = re.compile(r"Image has the following depend")88 markerEnd = re.compile(r"Summary")89 markerEnd2 = re.compile(r"Image has the following delay load depend")90 for line in r.stdout.split(os.linesep):91 if not started:92 if markerStart.search(line):93 started = True94 continue95 else: # Started parsing the libs96 line = line.strip()97 if not line:98 continue99 if markerEnd.search(line) or markerEnd2.search(line):100 break101 depsSet.add(line.lower())102 return list(depsSet)103 104 105def main():106 parser = argparse.ArgumentParser(description="Check library dependencies")107 parser.add_argument(108 "--bare",109 action="store_true",110 help="Produce plain, bare output: just a list"111 " of libraries, a library per line",112 )113 parser.add_argument(114 "--expected",115 metavar="CSV_LIST",116 help="CSV_LIST is a comma-separated list of expected"117 ' dependencies (or "none"). checks the specified'118 " library has only expected dependencies.",119 )120 121 parser.add_argument("library", help="The library file to check")122 commandArgs = parser.parse_args()123 # Get dependencies124 deps = []125 126 system = platform.system()127 if system == "Windows":128 deps = get_deps_link(commandArgs.library)129 elif system == "Darwin":130 deps = get_deps_otool(commandArgs.library)131 else:132 deps = get_deps_readelf(commandArgs.library)133 deps = sorted(deps)134 135 # If bare output specified, then just print the dependencies one per line136 if commandArgs.bare:137 print(os.linesep.join(deps))138 return139 140 # Calculate unexpected dependencies if expected list specified141 unexpected = []142 if commandArgs.expected:143 # none => any dependency is unexpected144 if commandArgs.expected == "none":145 unexpected = list(deps)146 else:147 expected = [d.strip() for d in commandArgs.expected.split(",")]148 unexpected = [d for d in deps if d not in expected]149 150 # Regular output151 print_info_line("Dependencies:")152 for dep in deps:153 print_info_line(" {}".format(dep))154 if unexpected:155 print_error_line("Unexpected Dependencies:")156 for dep in unexpected:157 print_error_line(" {}".format(dep))158 error("found unexpected dependencies")159 160 161if __name__ == "__main__":162 try:163 main()164 except ScriptError as e:165 print_error_line(str(e))166 sys.exit(1)167 168# end of file169