Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Add ifdef info to the stable ABI manifest
  • Loading branch information
encukou committed Apr 8, 2022
commit da1e76f27353cf6228ad619b41518b17e5aa9f76
22 changes: 22 additions & 0 deletions Misc/stable_abi.txt
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@
# value may change.
# - typedef: A C typedef which is used in other definitions in the limited API.
# Its size/layout/signature must not change.
# - ifdef: A feature macro: other items may be conditional on whether the macro
# is defined or not.

# Each top-level item can have details defined below it:
# - added: The version in which the item was added to the stable ABI.
Expand All @@ -41,6 +43,9 @@
# of the stable ABI.
# - a combination of the above (functions that were called by macros that
# were public in the past)
# - doc: for `ifdef`, the blurb added in documentation
# - windows: for `ifdef`, this macro is defined on Windows. (This info is used
# to generate the DLL manifest and needs to be available on all platforms.)

# For structs, one of the following must be set:
# - opaque: The struct name is available in the Limited API, but its members
Expand All @@ -59,6 +64,23 @@
# https://docs.python.org/3/c-api/stable.html#stable


# Feature macros for optional functionality:

ifdef MS_WINDOWS
doc on Windows
windows
ifdef HAVE_FORK
doc on platforms with fork()
ifdef USE_STACKCHECK
doc on platforms with USE_STACKCHECK
windows
ifdef PY_HAVE_THREAD_NATIVE_ID
doc on platforms with native thread IDs
windows
ifdef Py_REF_DEBUG
doc when Python is compiled in debug mode (with Py_REF_DEBUG)


# Mentioned in PEP 384:

struct PyObject
Expand Down
52 changes: 30 additions & 22 deletions Tools/scripts/stable_abi.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,21 +45,6 @@
MACOS = (sys.platform == "darwin")
UNIXY = MACOS or (sys.platform == "linux") # XXX should this be "not Windows"?

IFDEF_DOC_NOTES = {
'MS_WINDOWS': 'on Windows',
'HAVE_FORK': 'on platforms with fork()',
'USE_STACKCHECK': 'on platforms with USE_STACKCHECK',
'PY_HAVE_THREAD_NATIVE_ID': 'on platforms with native thread IDs',
}

# To generate the DLL definition, we need to know which feature macros are
# defined on Windows. On all platforms.
# Best way to do that is to hardcode the list (and later test in on Windows).
WINDOWS_IFDEFS = frozenset({
'MS_WINDOWS',
'PY_HAVE_THREAD_NATIVE_ID',
'USE_STACKCHECK',
})

# The stable ABI manifest (Misc/stable_abi.txt) exists only to fill the
# following dataclasses.
Expand Down Expand Up @@ -130,9 +115,11 @@ class ABIItem:
ifdef: str = None
struct_abi_kind: str = None
members: list = None
doc: str = None
windows: bool = False

KINDS = frozenset({
'struct', 'function', 'macro', 'data', 'const', 'typedef',
'struct', 'function', 'macro', 'data', 'const', 'typedef', 'ifdef',
})

def dump(self, indent=0):
Expand Down Expand Up @@ -171,8 +158,8 @@ def raise_error(msg):
levels.pop()
parent = levels[-1][0]
entry = None
if kind in ABIItem.KINDS:
if parent.kind not in {'manifest'}:
if parent.kind == 'manifest':
if kind not in kind in ABIItem.KINDS:
raise_error(f'{kind} cannot go in {parent.kind}')
entry = ABIItem(kind, content)
parent.add(entry)
Expand All @@ -193,10 +180,24 @@ def raise_error(msg):
parent.struct_abi_kind = kind
if kind == 'members':
parent.members = content.split()
elif kind in {'doc'}:
if parent.kind not in {'ifdef'}:
raise_error(f'{kind} cannot go in {parent.kind}')
parent.doc = content
elif kind in {'windows'}:
if parent.kind not in {'ifdef'}:
raise_error(f'{kind} cannot go in {parent.kind}')
parent.windows = True
else:
raise_error(f"unknown kind {kind!r}")
# When adding more, update the comment in stable_abi.txt.
levels.append((entry, level))

ifdef_names = {i.name for i in manifest.select({'ifdef'})}
for item in manifest.contents.values():
if item.ifdef and item.ifdef not in ifdef_names:
raise ValueError(f'{item.name} uses undeclared ifdef {item.ifdef}')

return manifest

# The tool can run individual "actions".
Expand Down Expand Up @@ -240,17 +241,20 @@ def gen_python3dll(manifest, args, outfile):
def sort_key(item):
return item.name.lower()

windows_ifdefs = {
item.name for item in manifest.select({'ifdef'}) if item.windows
}
for item in sorted(
manifest.select(
{'function'}, include_abi_only=True, ifdef=WINDOWS_IFDEFS),
{'function'}, include_abi_only=True, ifdef=windows_ifdefs),
key=sort_key):
write(f'EXPORT_FUNC({item.name})')

write()

for item in sorted(
manifest.select(
{'data'}, include_abi_only=True, ifdef=WINDOWS_IFDEFS),
{'data'}, include_abi_only=True, ifdef=windows_ifdefs),
key=sort_key):
write(f'EXPORT_DATA({item.name})')

Expand All @@ -273,7 +277,7 @@ def gen_doc_annotations(manifest, args, outfile):
writer.writeheader()
for item in manifest.select(REST_ROLES.keys(), include_abi_only=False):
if item.ifdef:
ifdef_note = IFDEF_DOC_NOTES[item.ifdef]
ifdef_note = manifest.contents[item.ifdef].doc
else:
ifdef_note = None
writer.writerow({
Expand Down Expand Up @@ -347,7 +351,11 @@ def test_windows_feature_macros(self):
write(" )")
write("")
write(f"EXPECTED_IFDEFS = set({sorted(ifdef_items)})")
write(f"WINDOWS_IFDEFS = { {k: k in WINDOWS_IFDEFS for k in ifdef_items} }")

windows_ifdef_values = {
name: manifest.contents[name].windows for name in ifdef_items
}
write(f"WINDOWS_IFDEFS = {windows_ifdef_values}")


def generate_or_check(manifest, args, path, func):
Expand Down