mirror of
https://github.com/react/react-native.git
synced 2026-09-28 13:23:09 +08:00
Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/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
262 lines
7.2 KiB
Python
262 lines
7.2 KiB
Python
# Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
#
|
|
# This source code is licensed under the MIT license found in the
|
|
# LICENSE file in the root directory of this source tree.
|
|
|
|
"""
|
|
Entry point for running the parser package as a script.
|
|
|
|
Usage:
|
|
# With output directory:
|
|
python ... --output-dir /path/to/output
|
|
"""
|
|
|
|
import argparse
|
|
import os
|
|
import shutil
|
|
import subprocess
|
|
import sys
|
|
import tempfile
|
|
|
|
from .config import ApiViewSnapshotConfig, parse_config_file
|
|
from .doxygen import get_doxygen_bin, run_doxygen
|
|
from .main import build_snapshot
|
|
from .path_utils import get_react_native_dir
|
|
from .snapshot_diff import check_snapshots
|
|
|
|
|
|
def run_command(
|
|
cmd: list[str],
|
|
label: str,
|
|
verbose: bool = False,
|
|
**kwargs,
|
|
) -> subprocess.CompletedProcess:
|
|
"""Run a subprocess command with consistent error handling."""
|
|
result = subprocess.run(cmd, **kwargs)
|
|
if result.returncode != 0:
|
|
if verbose:
|
|
print(f"{label} finished with error: {result.stderr}")
|
|
sys.exit(1)
|
|
elif verbose:
|
|
print(f"{label} finished successfully")
|
|
return result
|
|
|
|
|
|
def build_codegen(platform: str, verbose: bool = False) -> str:
|
|
react_native_dir = os.path.join(get_react_native_dir(), "packages", "react-native")
|
|
|
|
run_command(
|
|
[
|
|
"node",
|
|
"./scripts/generate-codegen-artifacts.js",
|
|
"--path",
|
|
"./",
|
|
"--outputPath",
|
|
"./api/codegen",
|
|
"--targetPlatform",
|
|
platform,
|
|
"--forceOutputPath",
|
|
],
|
|
label="Codegen",
|
|
verbose=verbose,
|
|
cwd=react_native_dir,
|
|
)
|
|
|
|
return os.path.join(react_native_dir, "api", "codegen")
|
|
|
|
|
|
def build_snapshot_for_view(
|
|
api_view: str,
|
|
react_native_dir: str,
|
|
include_directories: list[str],
|
|
exclude_patterns: list[str],
|
|
definitions: dict[str, str | int],
|
|
output_dir: str,
|
|
codegen_platform: str | None = None,
|
|
verbose: bool = True,
|
|
input_filter: str = None,
|
|
) -> str:
|
|
if verbose:
|
|
print(f"Generating API view: {api_view}")
|
|
|
|
api_dir = os.path.join(react_native_dir, "api")
|
|
if os.path.exists(api_dir):
|
|
if verbose:
|
|
print(" Deleting existing output directory")
|
|
shutil.rmtree(api_dir)
|
|
|
|
if codegen_platform is not None:
|
|
codegen_dir = build_codegen(codegen_platform, verbose=verbose)
|
|
include_directories.append(codegen_dir)
|
|
elif verbose:
|
|
print(" Skipping codegen")
|
|
|
|
run_doxygen(
|
|
working_dir=react_native_dir,
|
|
include_directories=include_directories,
|
|
exclude_patterns=exclude_patterns,
|
|
definitions=definitions,
|
|
input_filter=input_filter,
|
|
verbose=verbose,
|
|
)
|
|
|
|
if verbose:
|
|
print(" Building snapshot")
|
|
|
|
snapshot = build_snapshot(os.path.join(api_dir, "xml"))
|
|
snapshot_string = snapshot.to_string()
|
|
|
|
output_file = os.path.join(output_dir, f"{api_view}Cxx.api")
|
|
os.makedirs(output_dir, exist_ok=True)
|
|
|
|
with open(output_file, "w") as f:
|
|
f.write("// @" + "generated by scripts/cxx-api\n\n")
|
|
f.write(snapshot_string)
|
|
|
|
return snapshot_string
|
|
|
|
|
|
def build_snapshots(
|
|
snapshot_configs: list[ApiViewSnapshotConfig],
|
|
react_native_dir: str,
|
|
output_dir: str,
|
|
input_filter: str | None,
|
|
verbose: bool,
|
|
view_filter: str | None = None,
|
|
is_test: bool = False,
|
|
) -> None:
|
|
if not is_test:
|
|
for config in snapshot_configs:
|
|
if view_filter and config.snapshot_name != view_filter:
|
|
continue
|
|
|
|
build_snapshot_for_view(
|
|
api_view=config.snapshot_name,
|
|
react_native_dir=react_native_dir,
|
|
include_directories=config.inputs,
|
|
exclude_patterns=config.exclude_patterns,
|
|
definitions=config.definitions,
|
|
output_dir=output_dir,
|
|
codegen_platform=config.codegen_platform,
|
|
verbose=verbose,
|
|
input_filter=input_filter,
|
|
)
|
|
else:
|
|
snapshot = build_snapshot_for_view(
|
|
api_view="Test",
|
|
react_native_dir=react_native_dir,
|
|
include_directories=[],
|
|
exclude_patterns=[],
|
|
definitions={},
|
|
output_dir=output_dir,
|
|
codegen_platform=None,
|
|
verbose=verbose,
|
|
input_filter=input_filter,
|
|
)
|
|
|
|
if verbose:
|
|
print(snapshot)
|
|
|
|
|
|
def get_default_snapshot_dir() -> str:
|
|
return os.path.join(get_react_native_dir(), "scripts", "cxx-api", "api-snapshots")
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(
|
|
description="Generate API snapshots from C++ headers"
|
|
)
|
|
parser.add_argument(
|
|
"--output-dir",
|
|
type=str,
|
|
help="Output directory for the snapshot",
|
|
)
|
|
parser.add_argument(
|
|
"--check",
|
|
action="store_true",
|
|
help="Generate snapshots to a temp directory and compare against committed ones",
|
|
)
|
|
parser.add_argument(
|
|
"--snapshot-dir",
|
|
type=str,
|
|
help="Directory containing committed snapshots for comparison (used with --check)",
|
|
)
|
|
parser.add_argument(
|
|
"--view",
|
|
type=str,
|
|
help="Name of the API view to generate",
|
|
)
|
|
parser.add_argument(
|
|
"--test",
|
|
action="store_true",
|
|
help="Run on the local test directory instead of the react-native directory",
|
|
)
|
|
args = parser.parse_args()
|
|
|
|
verbose = not args.check
|
|
|
|
doxygen_bin = get_doxygen_bin()
|
|
version_result = subprocess.run(
|
|
[doxygen_bin, "--version"],
|
|
capture_output=True,
|
|
text=True,
|
|
)
|
|
if verbose:
|
|
print(f"Using Doxygen {version_result.stdout.strip()} ({doxygen_bin})")
|
|
|
|
react_native_package_dir = (
|
|
os.path.join(get_react_native_dir(), "packages", "react-native")
|
|
if not args.test
|
|
else os.path.join(get_react_native_dir(), "scripts", "cxx-api", "manual_test")
|
|
)
|
|
if verbose:
|
|
print(f"Running in directory: {react_native_package_dir}")
|
|
|
|
input_filter_path = os.path.join(
|
|
get_react_native_dir(),
|
|
"scripts",
|
|
"cxx-api",
|
|
"parser",
|
|
"input_filters",
|
|
"main.py",
|
|
)
|
|
|
|
input_filter = None
|
|
if os.path.exists(input_filter_path):
|
|
input_filter = f"python3 {input_filter_path}"
|
|
|
|
config_path = os.path.join(
|
|
get_react_native_dir(), "scripts", "cxx-api", "config.yml"
|
|
)
|
|
snapshot_configs = parse_config_file(
|
|
config_path,
|
|
get_react_native_dir(),
|
|
)
|
|
|
|
with tempfile.TemporaryDirectory() as tmpdir:
|
|
snapshot_output_dir = (
|
|
tmpdir if args.check else args.output_dir or get_default_snapshot_dir()
|
|
)
|
|
|
|
build_snapshots(
|
|
output_dir=snapshot_output_dir,
|
|
verbose=not args.check,
|
|
snapshot_configs=snapshot_configs,
|
|
react_native_dir=react_native_package_dir,
|
|
input_filter=input_filter,
|
|
view_filter=args.view,
|
|
is_test=args.test,
|
|
)
|
|
|
|
if args.check:
|
|
snapshot_dir = args.snapshot_dir or get_default_snapshot_dir()
|
|
|
|
if not check_snapshots(snapshot_output_dir, snapshot_dir):
|
|
sys.exit(1)
|
|
|
|
print("All snapshot checks passed")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|