| #!/usr/bin/env python3 |
| |
| # Licensed to the Apache Software Foundation (ASF) under one or more |
| # contributor license agreements. See the NOTICE file distributed with |
| # this work for additional information regarding copyright ownership. |
| # The ASF licenses this file to you under the Apache License, Version |
| # 2.0 (the "License"); you may not use this file except in compliance |
| # with the License. You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or |
| # implied. See the License for the specific language governing |
| # permissions and limitations under the License. |
| |
| # Check for programs linked with libraries they don't in fact use. |
| # For example, if a program was linked with -lfoo but doesn't use any |
| # symbols from libfoo. |
| # |
| # The list of programs is gathered by scraping Automake files, which are |
| # themselves gathered from Autoconf. ldd does the actual business of |
| # checking for unused dependencies. |
| # |
| # There are a couple of manual exceptions listed below, either because |
| # we deliberately link with an unused library -- possibly for |
| # convenience -- or because I haven't figured out how to fix it yet. |
| # |
| # For now, this only checks the programs that we install, |
| # but it could potentially check our libraries as well. |
| |
| import os.path |
| import re |
| import subprocess |
| import sys |
| |
| config_files_re = re.compile(r'(?<=config_files=").*(?=")') |
| programs_re = re.compile(r'([^\n ]*_)PROGRAMS \+?= (.*)') |
| |
| |
| def get_dependencies(program): |
| args = [ |
| './libtool', '--mode=execute', 'ldd', '--unused', '--function-relocs', |
| program |
| ] |
| for dependency in subprocess.Popen(args, stdout=subprocess.PIPE).stdout: |
| dependency = dependency.decode('utf-8')[:-1] |
| if any( |
| map( |
| os.path.basename(dependency).startswith, |
| [ |
| 'libdl.so.', # Because we add -ldl to LIBS |
| 'libgcc_s.so.', |
| 'libm.so.', # Why does Libtool call ld with -lm? |
| 'libpthread.so.' # Because we add -lpthread to LIBS |
| ])): |
| continue |
| |
| # Why does Libtool call ld with -lcrypto -lresolv -lssl? |
| if os.path.basename(program) == 'traffic_manager': |
| if any( |
| map( |
| os.path.basename(dependency).startswith, [ |
| 'libcrypto.so.', |
| 'libresolv.so.', |
| 'libssl.so.' |
| ])): |
| continue |
| |
| if re.sub(r'\s+', '', dependency): |
| yield dependency |
| |
| |
| success = True |
| filename = 'config.status' |
| contents = open(filename).read() |
| config_files = config_files_re.search(contents).group(0) |
| for filename in config_files.split(): |
| filename = filename + '.am' |
| if os.path.exists(filename): |
| contents = open(filename).read() |
| contents = contents.replace('\\\n', '') |
| for prefix, programs in programs_re.findall(contents): |
| if prefix not in [ |
| 'EXTRA_', |
| 'check_', |
| 'noinst_' |
| ]: |
| for program in programs.split(): |
| program = os.path.join(os.path.dirname(filename), program) |
| if os.path.exists(program): |
| dependencies = list(get_dependencies(program)) |
| if len(dependencies) > 1: |
| success = False |
| print(program) |
| for dependency in dependencies: |
| print(dependency) |
| |
| if not success: |
| sys.exit(1) |