Skip to content

Commit 838168d

Browse files
Dawid Małeckimeta-codesync[bot]
authored andcommitted
Add CI workflow for validating C++ API snapshot (#56042)
Summary: Pull Request resolved: #56042 Adds CI workflow for validating whether the current C++ API snapshot is equivalent with the generated one. Changelog: [Internal] Differential Revision: D95963515
1 parent ebb0e45 commit 838168d

2 files changed

Lines changed: 106 additions & 12 deletions

File tree

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
name: Validate C++ API Snapshots
2+
3+
on:
4+
workflow_dispatch:
5+
pull_request:
6+
paths:
7+
- "packages/react-native/ReactCommon/**"
8+
- "packages/react-native/ReactAndroid/**"
9+
- "packages/react-native/React/**"
10+
- "packages/react-native/ReactApple/**"
11+
- "packages/react-native/Libraries/**"
12+
- "scripts/cxx-api/**"
13+
push:
14+
branches:
15+
- main
16+
- "*-stable"
17+
paths:
18+
- "packages/react-native/ReactCommon/**"
19+
- "packages/react-native/ReactAndroid/**"
20+
- "packages/react-native/React/**"
21+
- "packages/react-native/ReactApple/**"
22+
- "packages/react-native/Libraries/**"
23+
- "scripts/cxx-api/**"
24+
25+
env:
26+
DOXYGEN_VERSION: "1.16.1"
27+
28+
jobs:
29+
validate_cxx_api_snapshots:
30+
runs-on: ubuntu-latest
31+
if: github.repository == 'facebook/react-native'
32+
steps:
33+
- name: Checkout
34+
uses: actions/checkout@v6
35+
- name: Setup node.js
36+
uses: ./.github/actions/setup-node
37+
- name: Run yarn
38+
uses: ./.github/actions/yarn-install
39+
- name: Restore Doxygen cache
40+
id: cache-doxygen
41+
uses: actions/cache@v4
42+
with:
43+
path: /tmp/doxygen-${{ env.DOXYGEN_VERSION }}
44+
key: doxygen-${{ env.DOXYGEN_VERSION }}
45+
- name: Install Doxygen
46+
if: steps.cache-doxygen.outputs.cache-hit != 'true'
47+
shell: bash
48+
run: |
49+
DOXYGEN_URL="https://github.com/doxygen/doxygen/releases/download/Release_${DOXYGEN_VERSION//./_}/doxygen-${DOXYGEN_VERSION}.linux.bin.tar.gz"
50+
MAX_RETRIES=3
51+
for i in $(seq 1 $MAX_RETRIES); do
52+
echo "Attempt $i of $MAX_RETRIES: Installing Doxygen ${DOXYGEN_VERSION}..."
53+
curl -fsSL "$DOXYGEN_URL" -o /tmp/doxygen.tar.gz && \
54+
tar -xzf /tmp/doxygen.tar.gz -C /tmp && \
55+
echo "Doxygen installed successfully." && \
56+
break
57+
echo "Attempt $i failed."
58+
if [ $i -eq $MAX_RETRIES ]; then
59+
echo "All $MAX_RETRIES attempts failed."
60+
exit 1
61+
fi
62+
sleep 5
63+
done
64+
- name: Set DOXYGEN_BIN
65+
shell: bash
66+
run: echo "DOXYGEN_BIN=/tmp/doxygen-${DOXYGEN_VERSION}/bin/doxygen" >> "$GITHUB_ENV"
67+
- name: Set up Python
68+
uses: actions/setup-python@v5
69+
with:
70+
python-version: "3.12"
71+
- name: Install Python dependencies
72+
shell: bash
73+
run: pip install doxmlparser natsort pyyaml
74+
- name: Validate C++ API snapshots
75+
shell: bash
76+
continue-on-error: true
77+
run: yarn cxx-api-validate --output-dir /tmp/cxx-api-snapshots
78+
- name: Upload C++ API snapshots
79+
uses: actions/upload-artifact@v6
80+
with:
81+
name: cxx-api-snapshots
82+
path: /tmp/cxx-api-snapshots/

scripts/cxx-api/parser/__main__.py

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ def build_snapshot_for_view(
8484
exclude_patterns: list[str],
8585
definitions: dict[str, str | int],
8686
output_dir: str,
87-
codegen_platform: str | None = None,
87+
codegen_dir: str | None = None,
8888
verbose: bool = True,
8989
input_filter: str = None,
9090
work_dir: str | None = None,
@@ -97,14 +97,7 @@ def build_snapshot_for_view(
9797
if work_dir is None:
9898
work_dir = os.path.join(react_native_dir, "api")
9999

100-
if codegen_platform is not None:
101-
codegen_output = os.path.join(work_dir, "codegen")
102-
codegen_dir = build_codegen(
103-
codegen_platform,
104-
verbose=verbose,
105-
output_path=codegen_output,
106-
label=api_view,
107-
)
100+
if codegen_dir is not None:
108101
include_directories.append(codegen_dir)
109102
elif verbose:
110103
print(f"[{api_view}] Skipping codegen")
@@ -156,6 +149,23 @@ def build_snapshots(
156149
]
157150

158151
with tempfile.TemporaryDirectory(prefix="cxx-api-") as parent_tmp:
152+
# Run codegen once per unique platform before parallel snapshot generation.
153+
# Debug/release variants share the same codegen output, and running
154+
# multiple codegen processes in parallel causes race conditions
155+
# (e.g. concurrent yarn install in buildCodegenIfNeeded).
156+
codegen_dirs: dict[str, str] = {}
157+
for config in configs_to_build:
158+
platform = config.codegen_platform
159+
if platform is not None and platform not in codegen_dirs:
160+
codegen_output = os.path.join(parent_tmp, f"codegen-{platform}")
161+
os.makedirs(codegen_output, exist_ok=True)
162+
codegen_dirs[platform] = build_codegen(
163+
platform,
164+
verbose=verbose,
165+
output_path=codegen_output,
166+
label=platform,
167+
)
168+
159169
with concurrent.futures.ThreadPoolExecutor() as executor:
160170
futures = {}
161171
for config in configs_to_build:
@@ -169,7 +179,7 @@ def build_snapshots(
169179
exclude_patterns=config.exclude_patterns,
170180
definitions=config.definitions,
171181
output_dir=output_dir,
172-
codegen_platform=config.codegen_platform,
182+
codegen_dir=codegen_dirs.get(config.codegen_platform),
173183
verbose=verbose,
174184
input_filter=input_filter if config.input_filter else None,
175185
work_dir=work_dir,
@@ -200,7 +210,7 @@ def build_snapshots(
200210
exclude_patterns=[],
201211
definitions={},
202212
output_dir=output_dir,
203-
codegen_platform=None,
213+
codegen_dir=None,
204214
verbose=verbose,
205215
input_filter=input_filter,
206216
)
@@ -286,7 +296,9 @@ def main():
286296

287297
with tempfile.TemporaryDirectory() as tmpdir:
288298
snapshot_output_dir = (
289-
tmpdir if args.check else args.output_dir or get_default_snapshot_dir()
299+
args.output_dir or tmpdir
300+
if args.check
301+
else args.output_dir or get_default_snapshot_dir()
290302
)
291303

292304
build_snapshots(

0 commit comments

Comments
 (0)