1212"""
1313
1414import argparse
15+ import concurrent .futures
1516import os
16- import shutil
1717import subprocess
1818import sys
1919import tempfile
20+ import traceback
2021
2122from .config import ApiViewSnapshotConfig , parse_config_file
2223from .doxygen import get_doxygen_bin , run_doxygen
@@ -36,13 +37,20 @@ def run_command(
3637 if result .returncode != 0 :
3738 if verbose :
3839 print (f"{ label } finished with error: { result .stderr } " )
39- sys .exit (1 )
40+ raise RuntimeError (
41+ f"{ label } finished with error (exit code { result .returncode } )"
42+ )
4043 elif verbose :
4144 print (f"{ label } finished successfully" )
4245 return result
4346
4447
45- def build_codegen (platform : str , verbose : bool = False ) -> str :
48+ def build_codegen (
49+ platform : str ,
50+ verbose : bool = False ,
51+ output_path : str = "./api/codegen" ,
52+ label : str = "" ,
53+ ) -> str :
4654 react_native_dir = os .path .join (get_react_native_dir (), "packages" , "react-native" )
4755
4856 run_command (
@@ -52,17 +60,21 @@ def build_codegen(platform: str, verbose: bool = False) -> str:
5260 "--path" ,
5361 "./" ,
5462 "--outputPath" ,
55- "./api/codegen" ,
63+ output_path ,
5664 "--targetPlatform" ,
5765 platform ,
5866 "--forceOutputPath" ,
5967 ],
60- label = "Codegen" ,
68+ label = f"[ { label } ] Codegen" if label else "Codegen" ,
6169 verbose = verbose ,
6270 cwd = react_native_dir ,
71+ capture_output = True ,
72+ text = True ,
6373 )
6474
65- return os .path .join (react_native_dir , "api" , "codegen" )
75+ if os .path .isabs (output_path ):
76+ return output_path
77+ return os .path .join (react_native_dir , output_path )
6678
6779
6880def build_snapshot_for_view (
@@ -75,21 +87,29 @@ def build_snapshot_for_view(
7587 codegen_platform : str | None = None ,
7688 verbose : bool = True ,
7789 input_filter : str = None ,
90+ work_dir : str | None = None ,
7891) -> str :
7992 if verbose :
80- print (f"Generating API view: { api_view } " )
93+ print (f"[ { api_view } ] Generating API view" )
8194
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 )
95+ include_directories = list (include_directories )
96+
97+ if work_dir is None :
98+ work_dir = os .path .join (react_native_dir , "api" )
8799
88100 if codegen_platform is not None :
89- codegen_dir = build_codegen (codegen_platform , verbose = verbose )
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+ )
90108 include_directories .append (codegen_dir )
91109 elif verbose :
92- print (" Skipping codegen" )
110+ print (f"[{ api_view } ] Skipping codegen" )
111+
112+ config_file = f".doxygen.config.{ api_view } .generated"
93113
94114 run_doxygen (
95115 working_dir = react_native_dir ,
@@ -98,12 +118,15 @@ def build_snapshot_for_view(
98118 definitions = definitions ,
99119 input_filter = input_filter ,
100120 verbose = verbose ,
121+ output_dir = work_dir ,
122+ config_file = config_file ,
123+ label = api_view ,
101124 )
102125
103126 if verbose :
104- print (" Building snapshot" )
127+ print (f"[ { api_view } ] Building snapshot" )
105128
106- snapshot = build_snapshot (os .path .join (api_dir , "xml" ))
129+ snapshot = build_snapshot (os .path .join (work_dir , "xml" ))
107130 snapshot_string = snapshot .to_string ()
108131
109132 output_file = os .path .join (output_dir , f"{ api_view } Cxx.api" )
@@ -126,36 +149,66 @@ def build_snapshots(
126149 is_test : bool = False ,
127150) -> None :
128151 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 ,
152+ configs_to_build = [
153+ config
154+ for config in snapshot_configs
155+ if not view_filter or config .snapshot_name == view_filter
156+ ]
157+
158+ with tempfile .TemporaryDirectory (prefix = "cxx-api-" ) as parent_tmp :
159+ with concurrent .futures .ThreadPoolExecutor () as executor :
160+ futures = {}
161+ for config in configs_to_build :
162+ work_dir = os .path .join (parent_tmp , config .snapshot_name )
163+ os .makedirs (work_dir , exist_ok = True )
164+ future = executor .submit (
165+ build_snapshot_for_view ,
166+ api_view = config .snapshot_name ,
167+ react_native_dir = react_native_dir ,
168+ include_directories = config .inputs ,
169+ exclude_patterns = config .exclude_patterns ,
170+ definitions = config .definitions ,
171+ output_dir = output_dir ,
172+ codegen_platform = config .codegen_platform ,
173+ verbose = verbose ,
174+ input_filter = input_filter if config .input_filter else None ,
175+ work_dir = work_dir ,
176+ )
177+ futures [future ] = config .snapshot_name
178+
179+ errors = []
180+ for future in concurrent .futures .as_completed (futures ):
181+ view_name = futures [future ]
182+ try :
183+ future .result ()
184+ except Exception as e :
185+ errors .append ((view_name , e ))
186+ if verbose :
187+ print (
188+ f"[{ view_name } ] Error generating:\n "
189+ f"{ traceback .format_exc ()} "
190+ )
191+
192+ if errors :
193+ failed_views = ", " .join (name for name , _ in errors )
194+ raise RuntimeError (f"Failed to generate snapshots: { failed_views } " )
195+ else :
196+ with tempfile .TemporaryDirectory (prefix = "cxx-api-test-" ) as work_dir :
197+ snapshot = build_snapshot_for_view (
198+ api_view = "Test" ,
135199 react_native_dir = react_native_dir ,
136- include_directories = config . inputs ,
137- exclude_patterns = config . exclude_patterns ,
138- definitions = config . definitions ,
200+ include_directories = [] ,
201+ exclude_patterns = [] ,
202+ definitions = {} ,
139203 output_dir = output_dir ,
140- codegen_platform = config . codegen_platform ,
204+ codegen_platform = None ,
141205 verbose = verbose ,
142- input_filter = input_filter if config .input_filter else None ,
206+ input_filter = input_filter ,
207+ work_dir = work_dir ,
143208 )
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- )
156209
157- if verbose :
158- print (snapshot )
210+ if verbose :
211+ print (snapshot )
159212
160213
161214def get_default_snapshot_dir () -> str :
0 commit comments