Skip to content

Commit b3032d8

Browse files
coadometa-codesync[bot]
authored andcommitted
Refactor snapshot generator entry point (react#56090)
Summary: Pull Request resolved: react#56090 Changelog: [Internal] Refactors the `__main__.py` file of the snapshot generator: - moves doxygen related logic to a separate file - unifies snapshot generation branches - reduces duplication Reviewed By: cipolleschi Differential Revision: D96455701 fbshipit-source-id: 7306fd2612bb401cd4ff02688ccaf38ecc71f239
1 parent 59660bc commit b3032d8

2 files changed

Lines changed: 202 additions & 151 deletions

File tree

scripts/cxx-api/parser/__main__.py

Lines changed: 96 additions & 151 deletions
Original file line numberDiff line numberDiff line change
@@ -18,63 +18,34 @@
1818
import sys
1919
import tempfile
2020

21-
from .config import parse_config_file
21+
from .config import ApiViewSnapshotConfig, parse_config_file
22+
from .doxygen import get_doxygen_bin, run_doxygen
2223
from .main import build_snapshot
2324
from .path_utils import get_react_native_dir
2425
from .snapshot_diff import check_snapshots
2526

26-
DOXYGEN_CONFIG_FILE = ".doxygen.config.generated"
2727

28-
29-
def build_doxygen_config(
30-
directory: str,
31-
include_directories: list[str] = None,
32-
exclude_patterns: list[str] = None,
33-
definitions: dict[str, str | int] = None,
34-
input_filter: str = None,
35-
) -> None:
36-
if include_directories is None:
37-
include_directories = []
38-
if exclude_patterns is None:
39-
exclude_patterns = []
40-
if definitions is None:
41-
definitions = {}
42-
43-
include_directories_str = " ".join(include_directories)
44-
exclude_patterns_str = "\\\n".join(exclude_patterns)
45-
if len(exclude_patterns) > 0:
46-
exclude_patterns_str = f"\\\n{exclude_patterns_str}"
47-
48-
definitions_str = " ".join(
49-
[
50-
f'{key}="{value}"' if isinstance(value, str) else f"{key}={value}"
51-
for key, value in definitions.items()
52-
]
53-
)
54-
55-
input_filter_str = input_filter if input_filter else ""
56-
57-
# read the template file
58-
with open(os.path.join(directory, ".doxygen.config.template")) as f:
59-
template = f.read()
60-
61-
# replace the placeholders with the actual values
62-
config = (
63-
template.replace("${INPUTS}", include_directories_str)
64-
.replace("${EXCLUDE_PATTERNS}", exclude_patterns_str)
65-
.replace("${PREDEFINED}", definitions_str)
66-
.replace("${DOXYGEN_INPUT_FILTER}", input_filter_str)
67-
)
68-
69-
# write the config file
70-
with open(os.path.join(directory, DOXYGEN_CONFIG_FILE), "w") as f:
71-
f.write(config)
28+
def run_command(
29+
cmd: list[str],
30+
label: str,
31+
verbose: bool = False,
32+
**kwargs,
33+
) -> subprocess.CompletedProcess:
34+
"""Run a subprocess command with consistent error handling."""
35+
result = subprocess.run(cmd, **kwargs)
36+
if result.returncode != 0:
37+
if verbose:
38+
print(f"{label} finished with error: {result.stderr}")
39+
sys.exit(1)
40+
elif verbose:
41+
print(f"{label} finished successfully")
42+
return result
7243

7344

7445
def build_codegen(platform: str, verbose: bool = False) -> str:
7546
react_native_dir = os.path.join(get_react_native_dir(), "packages", "react-native")
7647

77-
result = subprocess.run(
48+
run_command(
7849
[
7950
"node",
8051
"./scripts/generate-codegen-artifacts.js",
@@ -86,17 +57,11 @@ def build_codegen(platform: str, verbose: bool = False) -> str:
8657
platform,
8758
"--forceOutputPath",
8859
],
60+
label="Codegen",
61+
verbose=verbose,
8962
cwd=react_native_dir,
9063
)
9164

92-
if result.returncode != 0:
93-
if verbose:
94-
print(f"Codegen finished with error: {result.stderr}")
95-
sys.exit(1)
96-
else:
97-
if verbose:
98-
print("Codegen finished successfully")
99-
10065
return os.path.join(react_native_dir, "api", "codegen")
10166

10267

@@ -110,67 +75,35 @@ def build_snapshot_for_view(
11075
codegen_platform: str | None = None,
11176
verbose: bool = True,
11277
input_filter: str = None,
113-
) -> None:
114-
# If there is already an output directory, delete it
115-
if os.path.exists(os.path.join(react_native_dir, "api")):
116-
if verbose:
117-
print("Deleting existing output directory")
118-
shutil.rmtree(os.path.join(react_native_dir, "api"))
119-
78+
) -> str:
12079
if verbose:
12180
print(f"Generating API view: {api_view}")
12281

82+
api_dir = os.path.join(react_native_dir, "api")
83+
if os.path.exists(api_dir):
84+
if verbose:
85+
print(" Deleting existing output directory")
86+
shutil.rmtree(api_dir)
87+
12388
if codegen_platform is not None:
12489
codegen_dir = build_codegen(codegen_platform, verbose=verbose)
12590
include_directories.append(codegen_dir)
12691
elif verbose:
127-
print("Skipping codegen")
128-
129-
if verbose:
130-
print("Generating Doxygen config file")
92+
print(" Skipping codegen")
13193

132-
build_doxygen_config(
133-
react_native_dir,
94+
run_doxygen(
95+
working_dir=react_native_dir,
13496
include_directories=include_directories,
13597
exclude_patterns=exclude_patterns,
13698
definitions=definitions,
13799
input_filter=input_filter,
100+
verbose=verbose,
138101
)
139102

140103
if verbose:
141-
print("Running Doxygen")
142-
if input_filter:
143-
print(f" Using input filter: {input_filter}")
104+
print(" Building snapshot")
144105

145-
# Run doxygen with the config file
146-
doxygen_bin = os.environ.get("DOXYGEN_BIN", "doxygen")
147-
148-
result = subprocess.run(
149-
[doxygen_bin, DOXYGEN_CONFIG_FILE],
150-
cwd=react_native_dir,
151-
capture_output=True,
152-
text=True,
153-
)
154-
155-
# Check the result
156-
if result.returncode != 0:
157-
if verbose:
158-
print(f"Doxygen finished with error: {result.stderr}")
159-
sys.exit(1)
160-
else:
161-
if verbose:
162-
print("Doxygen finished successfully")
163-
164-
# Delete the Doxygen config file
165-
if verbose:
166-
print("Deleting Doxygen config file")
167-
os.remove(os.path.join(react_native_dir, DOXYGEN_CONFIG_FILE))
168-
169-
if verbose:
170-
print("Building snapshot")
171-
172-
# build snapshot, convert to string, and save to file
173-
snapshot = build_snapshot(os.path.join(react_native_dir, "api", "xml"))
106+
snapshot = build_snapshot(os.path.join(api_dir, "xml"))
174107
snapshot_string = snapshot.to_string()
175108

176109
output_file = os.path.join(output_dir, f"{api_view}Cxx.api")
@@ -183,6 +116,52 @@ def build_snapshot_for_view(
183116
return snapshot_string
184117

185118

119+
def build_snapshots(
120+
snapshot_configs: list[ApiViewSnapshotConfig],
121+
react_native_dir: str,
122+
output_dir: str,
123+
input_filter: str | None,
124+
verbose: bool,
125+
view_filter: str | None = None,
126+
is_test: bool = False,
127+
) -> None:
128+
if not is_test:
129+
for config in snapshot_configs:
130+
if view_filter and config.snapshot_name != view_filter:
131+
continue
132+
133+
build_snapshot_for_view(
134+
api_view=config.snapshot_name,
135+
react_native_dir=react_native_dir,
136+
include_directories=config.inputs,
137+
exclude_patterns=config.exclude_patterns,
138+
definitions=config.definitions,
139+
output_dir=output_dir,
140+
codegen_platform=config.codegen_platform,
141+
verbose=verbose,
142+
input_filter=input_filter,
143+
)
144+
else:
145+
snapshot = build_snapshot_for_view(
146+
api_view="Test",
147+
react_native_dir=react_native_dir,
148+
include_directories=[],
149+
exclude_patterns=[],
150+
definitions={},
151+
output_dir=output_dir,
152+
codegen_platform=None,
153+
verbose=verbose,
154+
input_filter=input_filter,
155+
)
156+
157+
if verbose:
158+
print(snapshot)
159+
160+
161+
def get_default_snapshot_dir() -> str:
162+
return os.path.join(get_react_native_dir(), "scripts", "cxx-api", "api-snapshots")
163+
164+
186165
def main():
187166
parser = argparse.ArgumentParser(
188167
description="Generate API snapshots from C++ headers"
@@ -216,7 +195,7 @@ def main():
216195

217196
verbose = not args.check
218197

219-
doxygen_bin = os.environ.get("DOXYGEN_BIN", "doxygen")
198+
doxygen_bin = get_doxygen_bin()
220199
version_result = subprocess.run(
221200
[doxygen_bin, "--version"],
222201
capture_output=True,
@@ -225,7 +204,6 @@ def main():
225204
if verbose:
226205
print(f"Using Doxygen {version_result.stdout.strip()} ({doxygen_bin})")
227206

228-
# Define the path to the react-native directory
229207
react_native_package_dir = (
230208
os.path.join(get_react_native_dir(), "packages", "react-native")
231209
if not args.test
@@ -247,7 +225,6 @@ def main():
247225
if os.path.exists(input_filter_path):
248226
input_filter = f"python3 {input_filter_path}"
249227

250-
# Parse config file
251228
config_path = os.path.join(
252229
get_react_native_dir(), "scripts", "cxx-api", "config.yml"
253230
)
@@ -256,60 +233,28 @@ def main():
256233
get_react_native_dir(),
257234
)
258235

259-
def build_snapshots(output_dir: str, verbose: bool) -> None:
260-
if not args.test:
261-
for config in snapshot_configs:
262-
if args.view and config.snapshot_name != args.view:
263-
continue
264-
265-
build_snapshot_for_view(
266-
api_view=config.snapshot_name,
267-
react_native_dir=react_native_package_dir,
268-
include_directories=config.inputs,
269-
exclude_patterns=config.exclude_patterns,
270-
definitions=config.definitions,
271-
output_dir=output_dir,
272-
codegen_platform=config.codegen_platform,
273-
verbose=verbose,
274-
input_filter=input_filter,
275-
)
276-
else:
277-
snapshot = build_snapshot_for_view(
278-
api_view="Test",
279-
react_native_dir=react_native_package_dir,
280-
include_directories=[],
281-
exclude_patterns=[],
282-
definitions={},
283-
output_dir=output_dir,
284-
codegen_platform=None,
285-
verbose=verbose,
286-
input_filter=input_filter,
287-
)
288-
289-
if verbose:
290-
print(snapshot)
236+
with tempfile.TemporaryDirectory() as tmpdir:
237+
snapshot_output_dir = (
238+
tmpdir if args.check else args.output_dir or get_default_snapshot_dir()
239+
)
291240

292-
if args.check:
293-
with tempfile.TemporaryDirectory() as tmpdir:
294-
build_snapshots(tmpdir, verbose=False)
241+
build_snapshots(
242+
output_dir=snapshot_output_dir,
243+
verbose=not args.check,
244+
snapshot_configs=snapshot_configs,
245+
react_native_dir=react_native_package_dir,
246+
input_filter=input_filter,
247+
view_filter=args.view,
248+
is_test=args.test,
249+
)
295250

296-
snapshot_dir = args.snapshot_dir or os.path.join(
297-
get_react_native_dir(), "scripts", "cxx-api", "api-snapshots"
298-
)
251+
if args.check:
252+
snapshot_dir = args.snapshot_dir or get_default_snapshot_dir()
299253

300-
if not check_snapshots(tmpdir, snapshot_dir):
254+
if not check_snapshots(snapshot_output_dir, snapshot_dir):
301255
sys.exit(1)
302256

303257
print("All snapshot checks passed")
304-
else:
305-
output_dir = (
306-
args.output_dir
307-
if args.output_dir
308-
else os.path.join(
309-
get_react_native_dir(), "scripts", "cxx-api", "api-snapshots"
310-
)
311-
)
312-
build_snapshots(output_dir, verbose=True)
313258

314259

315260
if __name__ == "__main__":

0 commit comments

Comments
 (0)