1818import sys
1919import 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
2223from .main import build_snapshot
2324from .path_utils import get_react_native_dir
2425from .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
7445def 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+
186165def 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
315260if __name__ == "__main__" :
0 commit comments