1
1
# This file is part of Cantera. See License.txt in the top-level directory or
2
2
# at https://cantera.org/license.txt for license and copyright information.
3
3
4
- from itertools import starmap
5
4
from pathlib import Path
6
5
import sys
7
6
import logging
8
7
from typing import List , Dict
9
8
import re
10
- import textwrap
9
+
10
+ from jinja2 import Environment , BaseLoader
11
11
12
12
from ._dataclasses import CsFunc
13
13
from ._Config import Config
14
- from .._helpers import normalize_indent , hanging_text
15
14
from .._dataclasses import Func , Param , HeaderFile , ArgList
16
15
from .._SourceGenerator import SourceGenerator
17
16
18
17
19
18
_logger = logging .getLogger ()
19
+ _loader = Environment (loader = BaseLoader )
20
20
21
21
class CSharpSourceGenerator (SourceGenerator ):
22
22
"""The SourceGenerator for scaffolding C# files for the .NET interface"""
23
23
24
- def _get_interop_func_text (self , func : CsFunc ) -> str :
25
- ret = f"{ self ._config .func_prolog } "
26
- if func .unsafe ():
27
- ret += "unsafe "
28
- ret += f"{ func .declaration ()} ;" # function text
29
- return ret
30
-
31
- @staticmethod
32
- def _get_base_handle_text (class_name : str , release_func_name : str ) -> str :
33
- handle = normalize_indent (f"""
34
- class { class_name } : CanteraHandle
35
- {{
36
- protected override bool ReleaseHandle() =>
37
- LibCantera.{ release_func_name } (Value) == InteropConsts.Success;
38
- }}
39
- """ )
40
-
41
- return handle
42
-
43
- @staticmethod
44
- def _get_derived_handle_text (derived_class_name : str , base_class_name : str ) -> str :
45
- derived_text = f"""class { derived_class_name } : { base_class_name } {{ }}"""
46
-
47
- return derived_text
48
-
49
-
50
24
def _get_property_text (self , clib_area : str , c_name : str , cs_name : str ,
51
25
known_funcs : Dict [str , CsFunc ]) -> str :
52
26
getter = known_funcs .get (clib_area + "_" + c_name )
@@ -61,58 +35,37 @@ def _get_property_text(self, clib_area: str, c_name: str, cs_name: str,
61
35
# from which we determine the appropriate C# type
62
36
prop_type = self ._config .prop_type_crosswalk [getter .arglist [- 1 ].p_type ]
63
37
64
- setter = known_funcs .get (clib_area + "_set" + c_name .capitalize ())
38
+ setter = known_funcs .get (
39
+ clib_area + "_set" + c_name .capitalize (),
40
+ CsFunc ("" , "" , "" , "" , "" ))
65
41
66
42
if prop_type in ["int" , "double" ]:
67
- text = f"""
68
- public { prop_type } { cs_name }
69
- {{
70
- get => InteropUtil.CheckReturn(
71
- LibCantera.{ getter .name } (_handle));"""
72
-
73
- if setter :
74
- text += f"""
75
- set => InteropUtil.CheckReturn(
76
- LibCantera.{ setter .name } (_handle, value));"""
77
-
78
- text += """
79
- }
80
- """
81
- elif prop_type == "string" :
82
- p_type = getter .arglist [1 ].p_type
43
+ template = _loader .from_string (self ._templates ["csharp-property-int-double" ])
44
+ return template .render (
45
+ prop_type = prop_type , cs_name = cs_name ,
46
+ getter = getter .name , setter = setter .name )
83
47
48
+ if prop_type == "string" :
84
49
# for get-string type functions we need to look up the type of the second
85
50
# (index 1) param for a cast because sometimes it"s an int and other times
86
51
# its a nuint (size_t)
87
- text = f"""
88
- public unsafe string { cs_name }
89
- {{
90
- get => InteropUtil.GetString(40, (length, buffer) =>
91
- LibCantera.{ getter .name } (_handle, ({ p_type } ) length, buffer));
92
- """
93
-
94
- if setter :
95
- text += f"""
96
- set => InteropUtil.CheckReturn(
97
- LibCantera.{ setter .name } (_handle, value));"""
98
-
99
- text += """
100
- }
101
- """
102
- else :
103
- _logger .critical (f"Unable to scaffold properties of type { prop_type !r} !" )
104
- sys .exit (1 )
52
+ template = _loader .from_string (self ._templates ["csharp-property-string" ])
53
+ return template .render (
54
+ cs_name = cs_name , p_type = getter .arglist [1 ].p_type ,
55
+ getter = getter .name , setter = setter .name )
105
56
106
- return normalize_indent (text )
57
+ _logger .critical (f"Unable to scaffold properties of type { prop_type !r} !" )
58
+ sys .exit (1 )
107
59
108
- def __init__ (self , out_dir : str , config : dict ):
60
+ def __init__ (self , out_dir : str , config : dict , templates : dict ):
109
61
if not out_dir :
110
62
_logger .critical ("Non-empty string identifying output path required." )
111
63
sys .exit (1 )
112
64
self ._out_dir = Path (out_dir )
113
65
114
66
# use the typed config
115
67
self ._config = Config .from_parsed (** config )
68
+ self ._templates = templates
116
69
117
70
def _get_wrapper_class_name (self , clib_area : str ) -> str :
118
71
return self ._config .class_crosswalk [clib_area ]
@@ -197,94 +150,59 @@ def _convert_func(self, parsed: Func) -> CsFunc:
197
150
198
151
return func
199
152
200
- def _write_file (self , filename : str , contents : str ):
201
- _logger .info (f" writing { filename !r} " )
153
+ def _write_file (self , file_name : str , template_name : str , ** kwargs ) -> None :
154
+ _logger .info (f" writing { file_name !r} " )
155
+ template = _loader .from_string (self ._templates ["csharp-preamble" ])
156
+ preamble = template .render (file_name = file_name )
202
157
203
- self ._out_dir .joinpath (filename ).write_text (contents , encoding = "utf-8" )
158
+ template = _loader .from_string (self ._templates [template_name ])
159
+ contents = template .render (preamble = preamble , ** kwargs )
204
160
205
- def _scaffold_interop (self , header_file_path : Path , cs_funcs : List [CsFunc ]):
206
- functions_text = "\n \n " .join (map (self ._get_interop_func_text , cs_funcs ))
207
-
208
- interop_text = textwrap .dedent (f"""
209
- { hanging_text (self ._config .preamble , 12 )}
210
-
211
- using System.Runtime.InteropServices;
212
-
213
- namespace Cantera.Interop;
161
+ self ._out_dir .joinpath (file_name ).write_text (contents , encoding = "utf-8" )
214
162
215
- static partial class LibCantera
216
- {{
217
- { hanging_text ( functions_text , 16 ) }
218
- }}
219
- """ ). lstrip ()
163
+ def _scaffold_interop ( self , header_file_path : Path , cs_funcs : List [ CsFunc ]):
164
+ template = _loader . from_string ( self . _templates [ "csharp-interop-func" ])
165
+ function_list = [
166
+ template . render ( unsafe = func . unsafe (), declaration = func . declaration ())
167
+ for func in cs_funcs ]
220
168
221
- self ._write_file ("Interop.LibCantera." + header_file_path .name + ".g.cs" ,
222
- interop_text )
169
+ file_name = "Interop.LibCantera." + header_file_path .name + ".g.cs"
170
+ self ._write_file (
171
+ file_name , "csharp-scaffold-interop" , cs_functions = function_list )
223
172
224
173
def _scaffold_handles (self , header_file_path : Path , handles : Dict [str , str ]):
225
- handles_text = "\n \n " .join (starmap (self ._get_base_handle_text , handles .items ()))
226
-
227
- handles_text = textwrap .dedent (f"""
228
- { hanging_text (self ._config .preamble , 12 )}
229
-
230
- namespace Cantera.Interop;
231
-
232
- { hanging_text (handles_text , 12 )}
233
- """ ).lstrip ()
174
+ template = _loader .from_string (self ._templates ["csharp-base-handle" ])
175
+ handle_list = [
176
+ template .render (class_name = key , release_func_name = val )
177
+ for key , val in handles .items ()]
234
178
235
- self ._write_file ("Interop.Handles." + header_file_path .name + ".g.cs" ,
236
- handles_text )
179
+ file_name = "Interop.Handles." + header_file_path .name + ".g.cs"
180
+ self ._write_file (
181
+ file_name , "csharp-scaffold-handles" , cs_handles = handle_list )
237
182
238
183
def _scaffold_derived_handles (self ):
239
- derived_handles = "\n \n " .join (starmap (self ._get_derived_handle_text ,
240
- self ._config .derived_handles .items ()))
184
+ template = _loader .from_string (self ._templates ["csharp-derived-handle" ])
185
+ handle_list = [
186
+ template .render (derived_class_name = key , base_class_name = val )
187
+ for key , val in self ._config .derived_handles .items ()]
241
188
242
- derived_handles_text = textwrap .dedent (f"""
243
- { hanging_text (self ._config .preamble , 12 )}
244
-
245
- namespace Cantera.Interop;
246
-
247
- { hanging_text (derived_handles , 12 )}
248
- """ ).lstrip ()
249
-
250
- self ._write_file ("Interop.Handles.g.cs" , derived_handles_text )
189
+ file_name = "Interop.Handles.g.cs"
190
+ self ._write_file (
191
+ file_name , "csharp-scaffold-handles" , cs_handles = handle_list )
251
192
252
193
def _scaffold_wrapper_class (self , clib_area : str , props : Dict [str , str ],
253
194
known_funcs : Dict [str , CsFunc ]):
254
- wrapper_class_name = self ._get_wrapper_class_name (clib_area )
255
- handle_class_name = self ._get_handle_class_name (clib_area )
256
-
257
- properties_text = "\n \n " .join (
195
+ property_list = [
258
196
self ._get_property_text (clib_area , c_name , cs_name , known_funcs )
259
- for (c_name , cs_name ) in props .items ())
260
-
261
- wrapper_class_text = textwrap .dedent (f"""
262
- { hanging_text (self ._config .preamble , 12 )}
263
-
264
- using Cantera.Interop;
197
+ for c_name , cs_name in props .items ()]
265
198
266
- namespace Cantera;
267
-
268
- public partial class { wrapper_class_name } : IDisposable
269
- {{
270
- readonly { handle_class_name } _handle;
271
-
272
- #pragma warning disable CS1591
273
-
274
- { hanging_text (properties_text , 16 )}
275
-
276
- #pragma warning restore CS1591
277
-
278
- /// <summary>
279
- /// Frees the underlying resources used by the
280
- /// native Cantera library for this instance.
281
- /// </summary>
282
- public void Dispose() =>
283
- _handle.Dispose();
284
- }}
285
- """ ).lstrip ()
286
-
287
- self ._write_file (wrapper_class_name + ".g.cs" , wrapper_class_text )
199
+ wrapper_class_name = self ._get_wrapper_class_name (clib_area )
200
+ handle_class_name = self ._get_handle_class_name (clib_area )
201
+ file_name = wrapper_class_name + ".g.cs"
202
+ self ._write_file (
203
+ file_name , "csharp-scaffold-wrapper-class" ,
204
+ wrapper_class_name = wrapper_class_name , handle_class_name = handle_class_name ,
205
+ cs_properties = property_list )
288
206
289
207
def generate_source (self , headers_files : List [HeaderFile ]):
290
208
self ._out_dir .mkdir (parents = True , exist_ok = True )
0 commit comments