Files
json/tools/generate_natvis/generate_natvis.py
T
Niels LohmannandKartikey 3f8677d028 Limit Natvis namespace variants to two ABI tags (#5877)
The generator wrote a namespace entry for every combination of ABI tags,
so nlohmann_json.natvis doubled with each new tag. With the 12 tags on
develop it had reached 57.9 MB, and one more tag would push it past
GitHub's 100 MB file limit. It now covers the default namespace, every
single tag, and every pair of tags (versioned and unversioned), which is
158 entries / 1 MB with 12 tags.

Taken over from #5868. Refs #5863.

Signed-off-by: Kartikey <kg704925@gmail.com>
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
Co-authored-by: Kartikey <kg704925@gmail.com>
2026-10-21 18:26:07 +02:00

96 lines
3.9 KiB
Python
Executable File

#!/usr/bin/env python3
import argparse
import itertools
import jinja2
import os
import re
import sys
# Directory of the repository, assuming this script stays at
# tools/generate_natvis/generate_natvis.py. Used only as the default value
# for the "output" argument below.
REPO_ROOT = os.path.normpath(os.path.join(sys.path[0], '..', '..'))
def semver(v):
if not re.fullmatch(r'\d+\.\d+\.\d+', v):
raise ValueError
return v
def abi_info(repo_root):
"""Derive the ABI tag list (in NLOHMANN_JSON_ABI_TAGS_CONCAT order) and the
library version from <repo_root>/include/nlohmann/detail/abi_macros.hpp,
so this script cannot drift from the header it visualizes."""
abi_macros_hpp = os.path.join(repo_root, 'include', 'nlohmann', 'detail', 'abi_macros.hpp')
with open(abi_macros_hpp) as f:
content = f.read()
# find the NLOHMANN_JSON_ABI_TAGS_CONCAT(...) invocation that lists the
# NLOHMANN_JSON_ABI_TAG_* identifiers in order (not its own #define, which
# only names its formal parameters a, b, c, ...)
tag_idents = None
for args in re.findall(r'NLOHMANN_JSON_ABI_TAGS_CONCAT\(\s*(.*?)\)', content, re.S):
idents = re.findall(r'NLOHMANN_JSON_ABI_TAG_\w+', args)
if idents:
tag_idents = idents
break
if not tag_idents:
raise ValueError(f'could not find NLOHMANN_JSON_ABI_TAGS_CONCAT(...) in {abi_macros_hpp}')
abi_tags = []
for ident in tag_idents:
# each tag is #define'd to its suffix (e.g. _diag) when the matching
# JSON_* option is enabled, and to nothing in the #else branch; only
# the non-empty definition matches here
match = re.search(r'#define\s+' + re.escape(ident) + r'\s+(_\w+)\s*\n', content)
if not match:
raise ValueError(f'could not find a non-empty #define for {ident} in {abi_macros_hpp}')
abi_tags.append(match.group(1))
version = {}
for part in ('MAJOR', 'MINOR', 'PATCH'):
match = re.search(r'#define\s+NLOHMANN_JSON_VERSION_' + part + r'\s+(\d+)', content)
if not match:
raise ValueError(f'could not find NLOHMANN_JSON_VERSION_{part} in {abi_macros_hpp}')
version[part] = match.group(1)
return abi_tags, '{MAJOR}.{MINOR}.{PATCH}'.format(**version)
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument('--version', type=semver,
help='Library version number (default: parsed from '
'include/nlohmann/detail/abi_macros.hpp below "output")')
parser.add_argument('output', nargs='?', default=REPO_ROOT,
help='Repository root: where include/nlohmann/detail/abi_macros.hpp is '
'read from and where nlohmann_json.natvis is written '
'(default: the repository root this script lives in)')
args = parser.parse_args()
derived_tags, derived_version = abi_info(args.output)
namespaces = ['nlohmann']
abi_prefix = 'json_abi'
abi_tags = derived_tags
version = '_v' + (args.version or derived_version).replace('.', '_')
inline_namespaces = []
# generate namespace names for up to two ABI tags
for n in range(0, min(len(abi_tags), 2) + 1):
for tags in itertools.combinations(abi_tags, n):
ns = abi_prefix + ''.join(tags)
inline_namespaces += [ns, ns + version]
namespaces += [f'{namespaces[0]}::{ns}' for ns in inline_namespaces]
env = jinja2.Environment(loader=jinja2.FileSystemLoader(searchpath=sys.path[0]), autoescape=True, trim_blocks=True,
lstrip_blocks=True, keep_trailing_newline=True)
template = env.get_template('nlohmann_json.natvis.j2')
natvis = template.render(namespaces=namespaces)
with open(os.path.join(args.output, 'nlohmann_json.natvis'), 'w') as f:
f.write(natvis)