SPIRV-Tools/utils/check_symbol_exports.py
Greg Roth 878b3b400b check_symbol_exports on Python3, Mac
subprocess.Popen returns byte data by default. Python2 was happy
to try to execute string operations on such data and hope for the
best, but python3 is more persnickety. Luckily, there's a simple
way to indicate to the Popen class that text data is wanted that
benefits the script. Just specifying universal_newlines will cause
the returned data to be text and also convert any system-specific
newlines to '\n' which the script relies on anyway.

Enabled on Mac as an incidental change after confirming that the
script works there just as well as it does on Linux.

It probably works on FreeBSD too, but I retired my BSD system years
ago. So I have no way to check.

Don't run it on Windows.
- It didn't work after all. It was just detecting non-posix and
  returning success.
2018-06-27 18:08:18 -04:00

94 lines
3.1 KiB
Python
Executable File

#!/usr/bin/env python
# Copyright (c) 2017 Google Inc.
# Licensed 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.
"""Checks names of global exports from a library."""
from __future__ import print_function
import os.path
import re
import subprocess
import sys
PROG = 'check_symbol_exports'
def command_output(cmd, directory):
"""Runs a command in a directory and returns its standard output stream.
Captures the standard error stream.
Raises a RuntimeError if the command fails to launch or otherwise fails.
"""
p = subprocess.Popen(cmd,
cwd=directory,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
universal_newlines=True)
(stdout, _) = p.communicate()
if p.returncode != 0:
raise RuntimeError('Failed to run %s in %s' % (cmd, directory))
return stdout
def check_library(library):
"""Scans the given library file for global exports. If all such
exports are namespaced or begin with spv (in either C or C++ styles)
then return 0. Otherwise emit a message and return 1."""
# The pattern for a global symbol record
symbol_pattern = re.compile(r'^[0-aA-Fa-f]+ g *F \.text.*[0-9A-Fa-f]+ +(.*)')
# Ok patterns are as follows, assuming Itanium name mangling:
# spv[A-Z] : extern "C" symbol starting with spv
# _ZN : something in a namespace
# _Z[0-9]+spv[A-Z_] : C++ symbol starting with spv[A-Z_]
symbol_ok_pattern = re.compile(r'^(spv[A-Z]|_ZN|_Z[0-9]+spv[A-Z_])')
seen = set()
result = 0
for line in command_output(['objdump', '-t', library], '.').split('\n'):
match = symbol_pattern.search(line)
if match:
symbol = match.group(1)
if symbol not in seen:
seen.add(symbol)
#print("look at '{}'".format(symbol))
if not symbol_ok_pattern.match(symbol):
print('{}: error: Unescaped exported symbol: {}'.format(PROG, symbol))
result = 1
return result
def main():
import argparse
parser = argparse.ArgumentParser(description='Check global names exported from a library')
parser.add_argument('library', help='The static library to examine')
args = parser.parse_args()
if not os.path.isfile(args.library):
print('{}: error: {} does not exist'.format(PROG, args.library))
sys.exit(1)
if os.name is 'posix':
status = check_library(args.library)
sys.exit(status)
else:
print('Passing test since not on Posix')
sys.exit(0)
if __name__ == '__main__':
main()