mirror of
https://github.com/python/cpython.git
synced 2025-08-04 08:59:19 +00:00
gh-97669: Create Tools/build/ directory (#97963)
Create Tools/build/ directory. Move the following scripts from Tools/scripts/ to Tools/build/: * check_extension_modules.py * deepfreeze.py * freeze_modules.py * generate_global_objects.py * generate_levenshtein_examples.py * generate_opcode_h.py * generate_re_casefix.py * generate_sre_constants.py * generate_stdlib_module_names.py * generate_token.py * parse_html5_entities.py * smelly.py * stable_abi.py * umarshal.py * update_file.py * verify_ensurepip_wheels.py Update references to these scripts.
This commit is contained in:
parent
eae7dad402
commit
1863302d61
41 changed files with 102 additions and 84 deletions
173
Tools/build/smelly.py
Executable file
173
Tools/build/smelly.py
Executable file
|
@ -0,0 +1,173 @@
|
|||
#!/usr/bin/env python
|
||||
# Script checking that all symbols exported by libpython start with Py or _Py
|
||||
|
||||
import os.path
|
||||
import subprocess
|
||||
import sys
|
||||
import sysconfig
|
||||
|
||||
|
||||
ALLOWED_PREFIXES = ('Py', '_Py')
|
||||
if sys.platform == 'darwin':
|
||||
ALLOWED_PREFIXES += ('__Py',)
|
||||
|
||||
IGNORED_EXTENSION = "_ctypes_test"
|
||||
# Ignore constructor and destructor functions
|
||||
IGNORED_SYMBOLS = {'_init', '_fini'}
|
||||
|
||||
|
||||
def is_local_symbol_type(symtype):
|
||||
# Ignore local 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":
|
||||
return True
|
||||
|
||||
# Ignore the initialized data section (d and D) and the BSS data
|
||||
# section. For example, ignore "__bss_start (type: B)"
|
||||
# and "_edata (type: D)".
|
||||
if symtype in "bBdD":
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def get_exported_symbols(library, dynamic=False):
|
||||
print(f"Check that {library} only exports symbols starting with Py or _Py")
|
||||
|
||||
# Only look at dynamic symbols
|
||||
args = ['nm', '--no-sort']
|
||||
if dynamic:
|
||||
args.append('--dynamic')
|
||||
args.append(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):
|
||||
smelly_symbols = []
|
||||
python_symbols = []
|
||||
local_symbols = []
|
||||
|
||||
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()
|
||||
symbol = parts[-1]
|
||||
result = '%s (type: %s)' % (symbol, symtype)
|
||||
|
||||
if symbol.startswith(ALLOWED_PREFIXES):
|
||||
python_symbols.append(result)
|
||||
continue
|
||||
|
||||
if is_local_symbol_type(symtype):
|
||||
local_symbols.append(result)
|
||||
elif symbol in IGNORED_SYMBOLS:
|
||||
local_symbols.append(result)
|
||||
else:
|
||||
smelly_symbols.append(result)
|
||||
|
||||
if local_symbols:
|
||||
print(f"Ignore {len(local_symbols)} local symbols")
|
||||
return smelly_symbols, python_symbols
|
||||
|
||||
|
||||
def check_library(library, dynamic=False):
|
||||
nm_output = get_exported_symbols(library, dynamic)
|
||||
smelly_symbols, python_symbols = get_smelly_symbols(nm_output)
|
||||
|
||||
if not smelly_symbols:
|
||||
print(f"OK: no smelly symbol found ({len(python_symbols)} Python symbols)")
|
||||
return 0
|
||||
|
||||
print()
|
||||
smelly_symbols.sort()
|
||||
for symbol in smelly_symbols:
|
||||
print("Smelly symbol: %s" % symbol)
|
||||
|
||||
print()
|
||||
print("ERROR: Found %s smelly symbols!" % len(smelly_symbols))
|
||||
return len(smelly_symbols)
|
||||
|
||||
|
||||
def check_extensions():
|
||||
print(__file__)
|
||||
# This assumes pybuilddir.txt is in same directory as pyconfig.h.
|
||||
# In the case of out-of-tree builds, we can't assume pybuilddir.txt is
|
||||
# in the source folder.
|
||||
config_dir = os.path.dirname(sysconfig.get_config_h_filename())
|
||||
filename = os.path.join(config_dir, "pybuilddir.txt")
|
||||
try:
|
||||
with open(filename, encoding="utf-8") as fp:
|
||||
pybuilddir = fp.readline()
|
||||
except FileNotFoundError:
|
||||
print(f"Cannot check extensions because {filename} does not exist")
|
||||
return True
|
||||
|
||||
print(f"Check extension modules from {pybuilddir} directory")
|
||||
builddir = os.path.join(config_dir, pybuilddir)
|
||||
nsymbol = 0
|
||||
for name in os.listdir(builddir):
|
||||
if not name.endswith(".so"):
|
||||
continue
|
||||
if IGNORED_EXTENSION in name:
|
||||
print()
|
||||
print(f"Ignore extension: {name}")
|
||||
continue
|
||||
|
||||
print()
|
||||
filename = os.path.join(builddir, name)
|
||||
nsymbol += check_library(filename, dynamic=True)
|
||||
|
||||
return nsymbol
|
||||
|
||||
|
||||
def main():
|
||||
nsymbol = 0
|
||||
|
||||
# static library
|
||||
LIBRARY = sysconfig.get_config_var('LIBRARY')
|
||||
if not LIBRARY:
|
||||
raise Exception("failed to get LIBRARY variable from sysconfig")
|
||||
if os.path.exists(LIBRARY):
|
||||
nsymbol += check_library(LIBRARY)
|
||||
|
||||
# dynamic library
|
||||
LDLIBRARY = sysconfig.get_config_var('LDLIBRARY')
|
||||
if not LDLIBRARY:
|
||||
raise Exception("failed to get LDLIBRARY variable from sysconfig")
|
||||
if LDLIBRARY != LIBRARY:
|
||||
print()
|
||||
nsymbol += check_library(LDLIBRARY, dynamic=True)
|
||||
|
||||
# Check extension modules like _ssl.cpython-310d-x86_64-linux-gnu.so
|
||||
nsymbol += check_extensions()
|
||||
|
||||
if nsymbol:
|
||||
print()
|
||||
print(f"ERROR: Found {nsymbol} smelly symbols in total!")
|
||||
sys.exit(1)
|
||||
|
||||
print()
|
||||
print(f"OK: all exported symbols of all libraries "
|
||||
f"are prefixed with {' or '.join(map(repr, ALLOWED_PREFIXES))}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
Loading…
Add table
Add a link
Reference in a new issue