#!/usr/bin/env python3
#
# //===----------------------------------------------------------------------===//
# //
# // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
# // See https://llvm.org/LICENSE.txt for license information.
# // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
# //
# //===----------------------------------------------------------------------===//
#
import argparse
import os
import platform
import re
import sys
from libomputils import (
ScriptError,
error,
execute_command,
print_info_line,
print_error_line,
)
def get_deps_readelf(filename):
"""Get list of dependencies from readelf"""
deps = []
# Force readelf call to be in English
os.environ["LANG"] = "C"
r = execute_command(["readelf", "-d", filename])
if r.returncode != 0:
error("readelf -d {} failed".format(filename))
neededRegex = re.compile(r"\(NEEDED\)\s+Shared library: \[([a-zA-Z0-9_.-]+)\]")
for line in r.stdout.split(os.linesep):
match = neededRegex.search(line)
if match:
deps.append(match.group(1))
return deps
def get_deps_otool(filename):
"""Get list of dependencies from otool"""
deps = []
r = execute_command(["otool", "-L", filename])
if r.returncode != 0:
error("otool -L {} failed".format(filename))
libRegex = re.compile(r"([^ \t]+)\s+\(compatibility version ")
thisLibRegex = re.compile(r"@rpath/{}".format(os.path.basename(filename)))
for line in r.stdout.split(os.linesep):
match = thisLibRegex.search(line)
if match:
# Don't include the library itself as a needed dependency
continue
match = libRegex.search(line)
if match:
deps.append(match.group(1))
continue
return deps
def get_deps_link(filename):
"""Get list of dependecies from link (Windows OS)"""
depsSet = set([])
f = filename.lower()
args = ["link", "/DUMP"]
if f.endswith(".lib"):
args.append("/DIRECTIVES")
elif f.endswith(".dll") or f.endswith(".exe"):
args.append("/DEPENDENTS")
else:
error("unrecognized file extension: {}".format(filename))
args.append(filename)
r = execute_command(args)
if r.returncode != 0:
error("{} failed".format(args.command))
if f.endswith(".lib"):
regex = re.compile(r"\s*[-/]defaultlib:(.*)\s*$")
for line in r.stdout.split(os.linesep):
line = line.lower()
match = regex.search(line)
if match:
depsSet.add(match.group(1))
else:
started = False
markerStart = re.compile(r"Image has the following depend")
markerEnd = re.compile(r"Summary")
markerEnd2 = re.compile(r"Image has the following delay load depend")
for line in r.stdout.split(os.linesep):
if not started:
if markerStart.search(line):
started = True
continue
else: # Started parsing the libs
line = line.strip()
if not line:
continue
if markerEnd.search(line) or markerEnd2.search(line):
break
depsSet.add(line.lower())
return list(depsSet)
def main():
parser = argparse.ArgumentParser(description="Check library dependencies")
parser.add_argument(
"--bare",
action="store_true",
help="Produce plain, bare output: just a list"
" of libraries, a library per line",
)
parser.add_argument(
"--expected",
metavar="CSV_LIST",
help="CSV_LIST is a comma-separated list of expected"
' dependencies (or "none"). checks the specified'
" library has only expected dependencies.",
)
parser.add_argument("library", help="The library file to check")
commandArgs = parser.parse_args()
# Get dependencies
deps = []
system = platform.system()
if system == "Windows":
deps = get_deps_link(commandArgs.library)
elif system == "Darwin":
deps = get_deps_otool(commandArgs.library)
else:
deps = get_deps_readelf(commandArgs.library)
deps = sorted(deps)
# If bare output specified, then just print the dependencies one per line
if commandArgs.bare:
print(os.linesep.join(deps))
return
# Calculate unexpected dependencies if expected list specified
unexpected = []
if commandArgs.expected:
# none => any dependency is unexpected
if commandArgs.expected == "none":
unexpected = list(deps)
else:
expected = [d.strip() for d in commandArgs.expected.split(",")]
unexpected = [d for d in deps if d not in expected]
# Regular output
print_info_line("Dependencies:")
for dep in deps:
print_info_line(" {}".format(dep))
if unexpected:
print_error_line("Unexpected Dependencies:")
for dep in unexpected:
print_error_line(" {}".format(dep))
error("found unexpected dependencies")
if __name__ == "__main__":
try:
main()
except ScriptError as e:
print_error_line(str(e))
sys.exit(1)
# end of file