mirror of
https://github.com/python/cpython.git
synced 2025-09-26 18:29:57 +00:00
bpo-31810: Add smelly.py to check exported symbols (#4057)
* Add Tools/scripts/smelly.py: script checking if all symbols exported by libpython start with "Py" or "_Py". * Modify "make smelly" to run smelly.py: the command now fails with a non-zero exit code if libpython leaks a "smelly" symbol. * Travis CI now runs "make smelly"
This commit is contained in:
parent
1aa00ff383
commit
87d332dcdb
3 changed files with 82 additions and 3 deletions
|
@ -91,6 +91,8 @@ script:
|
||||||
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then ./python Tools/scripts/patchcheck.py --travis $TRAVIS_PULL_REQUEST; fi
|
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then ./python Tools/scripts/patchcheck.py --travis $TRAVIS_PULL_REQUEST; fi
|
||||||
# `-r -w` implicitly provided through `make buildbottest`.
|
# `-r -w` implicitly provided through `make buildbottest`.
|
||||||
- make buildbottest TESTOPTS="-j4 -uall,-cpu"
|
- make buildbottest TESTOPTS="-j4 -uall,-cpu"
|
||||||
|
# Check that all symbols exported by libpython start with "Py" or "_Py"
|
||||||
|
- make smelly
|
||||||
|
|
||||||
notifications:
|
notifications:
|
||||||
email: false
|
email: false
|
||||||
|
|
|
@ -1659,10 +1659,9 @@ distclean: clobber
|
||||||
-o -name '*.bak' ')' \
|
-o -name '*.bak' ')' \
|
||||||
-exec rm -f {} ';'
|
-exec rm -f {} ';'
|
||||||
|
|
||||||
# Check for smelly exported symbols (not starting with Py/_Py)
|
# Check that all symbols exported by libpython start with "Py" or "_Py"
|
||||||
smelly: @DEF_MAKE_RULE@
|
smelly: @DEF_MAKE_RULE@
|
||||||
nm -p $(LIBRARY) | \
|
$(RUNSHARED) ./$(BUILDPYTHON) Tools/scripts/smelly.py
|
||||||
sed -n "/ [TDB] /s/.* //p" | grep -v "^_*Py" | sort -u; \
|
|
||||||
|
|
||||||
# Find files with funny names
|
# Find files with funny names
|
||||||
funny:
|
funny:
|
||||||
|
|
78
Tools/scripts/smelly.py
Executable file
78
Tools/scripts/smelly.py
Executable file
|
@ -0,0 +1,78 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
# Script checking that all symbols exported by libpython start with Py or _Py
|
||||||
|
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
import sysconfig
|
||||||
|
|
||||||
|
|
||||||
|
def get_exported_symbols():
|
||||||
|
LIBRARY = sysconfig.get_config_var('LIBRARY')
|
||||||
|
if not LIBRARY:
|
||||||
|
raise Exception("failed to get LIBRARY")
|
||||||
|
|
||||||
|
args = ('nm', '-p', LIBRARY)
|
||||||
|
print("+ %s" % ' '.join(args))
|
||||||
|
proc = subprocess.run(args, stdout=subprocess.PIPE, universal_newlines=True)
|
||||||
|
if proc.returncode:
|
||||||
|
sys.stdout.write(proc.stdout)
|
||||||
|
sys.exit(proc.returncode)
|
||||||
|
|
||||||
|
stdout = proc.stdout.rstrip()
|
||||||
|
if not stdout:
|
||||||
|
raise Exception("command output is empty")
|
||||||
|
return stdout
|
||||||
|
|
||||||
|
|
||||||
|
def get_smelly_symbols(stdout):
|
||||||
|
symbols = []
|
||||||
|
ignored_symtypes = set()
|
||||||
|
for line in stdout.splitlines():
|
||||||
|
# Split line '0000000000001b80 D PyTextIOWrapper_Type'
|
||||||
|
if not line:
|
||||||
|
continue
|
||||||
|
|
||||||
|
parts = line.split(maxsplit=2)
|
||||||
|
if len(parts) < 3:
|
||||||
|
continue
|
||||||
|
|
||||||
|
symtype = parts[1].strip()
|
||||||
|
# Ignore private symbols.
|
||||||
|
#
|
||||||
|
# If lowercase, the symbol is usually local; if uppercase, the symbol
|
||||||
|
# is global (external). There are however a few lowercase symbols that
|
||||||
|
# are shown for special global symbols ("u", "v" and "w").
|
||||||
|
if symtype.islower() and symtype not in "uvw":
|
||||||
|
ignored_symtypes.add(symtype)
|
||||||
|
continue
|
||||||
|
|
||||||
|
symbol = parts[-1]
|
||||||
|
if symbol.startswith(('Py', '_Py')):
|
||||||
|
continue
|
||||||
|
symbol = '%s (type: %s)' % (symbol, symtype)
|
||||||
|
symbols.append(symbol)
|
||||||
|
|
||||||
|
if ignored_symtypes:
|
||||||
|
print("Ignored symbol types: %s" % ', '.join(sorted(ignored_symtypes)))
|
||||||
|
print()
|
||||||
|
return symbols
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
nm_output = get_exported_symbols()
|
||||||
|
symbols = get_smelly_symbols(nm_output)
|
||||||
|
|
||||||
|
if not symbols:
|
||||||
|
print("OK: no smelly symbol found")
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
symbols.sort()
|
||||||
|
for symbol in symbols:
|
||||||
|
print("Smelly symbol: %s" % symbol)
|
||||||
|
print()
|
||||||
|
print("ERROR: Found %s smelly symbols!" % len(symbols))
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
Loading…
Add table
Add a link
Reference in a new issue