2 import os, sys, re, subprocess
5 print("USAGE: /path/to/lightning.h /path/to/bindings/output /path/to/bindings/ /path/to/bindings/output.c debug lang")
8 if sys.argv[5] == "false":
10 elif sys.argv[5] == "true":
13 print("debug should be true or false and indicates whether to track allocations and ensure we don't leak")
17 if sys.argv[6] == "java" or sys.argv[6] == "android":
19 from java_strings import Consts
20 target = java_strings.Target.JAVA
21 if sys.argv[6] == "android":
22 target = java_strings.Target.ANDROID
23 elif sys.argv[6] == "typescript":
24 import typescript_strings
25 from typescript_strings import Consts
26 target = typescript_strings.Target.NODEJS
27 if len(sys.argv) == 8 and sys.argv[7] == 'browser':
28 target = typescript_strings.Target.BROWSER
30 print("Only java or typescript can be set for lang")
34 consts = Consts(DEBUG, target=target, outdir=sys.argv[4])
36 local_git_version = os.getenv("LDK_GARBAGECOLLECTED_GIT_OVERRIDE")
37 if local_git_version is None:
38 local_git_version = subprocess.check_output(["git", "describe", '--tag', '--dirty']).decode("utf-8").strip()
40 from bindingstypes import *
47 def camel_to_snake(s):
48 # Convert camel case to snake case, in a way that appears to match cbindgen
54 if lastchar.isupper():
55 if not char.isupper() and not lastund:
60 ret = ret + lastchar.lower()
63 if char.isupper() and not lastund:
71 return (ret + lastchar.lower()).strip("_")
73 def doc_to_field_nullable(doc):
76 for line in doc.splitlines():
77 if "Note that this (or a relevant inner pointer) may be NULL or all-0s to represent None" in line:
81 def doc_to_params_ret_nullable(doc):
86 for line in doc.splitlines():
87 if "may be NULL or all-0s to represent None" not in line:
89 if "Note that the return value" in line:
91 elif "Note that " in line:
92 param = line.split("Note that ")[1].split(" ")[0]
94 return (params, ret_null)
97 # Map from enum name to "contains trait object"
99 opaque_structs = set()
104 var_is_arr_regex = re.compile("\(\* ?([A-za-z0-9_]*)\)\[([a-z0-9]*)\]")
105 var_ty_regex = re.compile("([A-za-z_0-9]*)(.*)")
106 java_c_types_none_allowed = True # Unset when we do the real pass that populates the above sets
107 def java_c_types(fn_arg, ret_arr_len):
108 fn_arg = fn_arg.strip()
109 if fn_arg.startswith("MUST_USE_RES "):
112 if fn_arg.startswith("const "):
115 if fn_arg.startswith("struct "):
117 if fn_arg.startswith("enum "):
119 nonnull_ptr = "NONNULL_PTR" in fn_arg
120 fn_arg = fn_arg.replace("NONNULL_PTR", "")
127 if fn_arg.startswith("LDKThirtyTwoBytes"):
128 fn_arg = "uint8_t (*" + fn_arg[18:] + ")[32]"
129 assert var_is_arr_regex.match(fn_arg[8:])
130 rust_obj = "LDKThirtyTwoBytes"
132 elif fn_arg.startswith("LDKTxid"):
133 fn_arg = "uint8_t (*" + fn_arg[8:] + ")[32]"
134 assert var_is_arr_regex.match(fn_arg[8:])
135 rust_obj = "LDKThirtyTwoBytes"
137 elif fn_arg.startswith("LDKPublicKey"):
138 fn_arg = "uint8_t (*" + fn_arg[13:] + ")[33]"
139 assert var_is_arr_regex.match(fn_arg[8:])
140 rust_obj = "LDKPublicKey"
141 arr_access = "compressed_form"
142 elif fn_arg.startswith("LDKSecretKey"):
143 fn_arg = "uint8_t (*" + fn_arg[13:] + ")[32]"
144 assert var_is_arr_regex.match(fn_arg[8:])
145 rust_obj = "LDKSecretKey"
147 elif fn_arg.startswith("LDKSignature"):
148 fn_arg = "uint8_t (*" + fn_arg[13:] + ")[64]"
149 assert var_is_arr_regex.match(fn_arg[8:])
150 rust_obj = "LDKSignature"
151 arr_access = "compact_form"
152 elif fn_arg.startswith("LDKRecoverableSignature"):
153 fn_arg = "uint8_t (*" + fn_arg[24:] + ")[68]"
154 assert var_is_arr_regex.match(fn_arg[8:])
155 rust_obj = "LDKRecoverableSignature"
156 arr_access = "serialized_form"
157 elif fn_arg.startswith("LDKThreeBytes"):
158 fn_arg = "uint8_t (*" + fn_arg[14:] + ")[3]"
159 assert var_is_arr_regex.match(fn_arg[8:])
160 rust_obj = "LDKThreeBytes"
162 elif fn_arg.startswith("LDKFourBytes"):
163 fn_arg = "uint8_t (*" + fn_arg[13:] + ")[4]"
164 assert var_is_arr_regex.match(fn_arg[8:])
165 rust_obj = "LDKFourBytes"
167 elif fn_arg.startswith("LDKSixteenBytes"):
168 fn_arg = "uint8_t (*" + fn_arg[16:] + ")[16]"
169 assert var_is_arr_regex.match(fn_arg[8:])
170 rust_obj = "LDKSixteenBytes"
172 elif fn_arg.startswith("LDKTwentyBytes"):
173 fn_arg = "uint8_t (*" + fn_arg[15:] + ")[20]"
174 assert var_is_arr_regex.match(fn_arg[8:])
175 rust_obj = "LDKTwentyBytes"
177 elif fn_arg.startswith("LDKTwelveBytes"):
178 fn_arg = "uint8_t (*" + fn_arg[15:] + ")[12]"
179 assert var_is_arr_regex.match(fn_arg[8:])
180 rust_obj = "LDKTwelveBytes"
182 elif fn_arg.startswith("LDKu8slice"):
183 fn_arg = "uint8_t (*" + fn_arg[11:] + ")[datalen]"
184 assert var_is_arr_regex.match(fn_arg[8:])
185 rust_obj = "LDKu8slice"
187 elif fn_arg.startswith("LDKCVec_u8Z"):
188 fn_arg = "uint8_t (*" + fn_arg[12:] + ")[datalen]"
189 rust_obj = "LDKCVec_u8Z"
190 assert var_is_arr_regex.match(fn_arg[8:])
192 elif fn_arg.startswith("LDKTransaction ") or fn_arg == "LDKTransaction":
193 fn_arg = "uint8_t (*" + fn_arg[15:] + ")[datalen]"
194 rust_obj = "LDKTransaction"
195 assert var_is_arr_regex.match(fn_arg[8:])
197 elif fn_arg.startswith("LDKCVec_"):
200 fn_arg = fn_arg.replace("*", "")
203 tyn = fn_arg[8:].split(" ")
204 assert tyn[0].endswith("Z")
208 new_arg = "LDK" + tyn[0][:-1]
210 new_arg = new_arg + " " + a
211 res = java_c_types(new_arg, ret_arr_len)
213 assert java_c_types_none_allowed
216 res.pass_by_ref = True
217 if res.is_native_primitive or res.passed_as_ptr:
218 return TypeInfo(rust_obj=fn_arg.split(" ")[0], java_ty=res.java_ty + "[]", java_hu_ty=res.java_hu_ty + "[]",
219 java_fn_ty_arg="[" + res.java_fn_ty_arg, c_ty=res.c_ty + "Array", passed_as_ptr=False, is_ptr=is_ptr,
220 nonnull_ptr=nonnull_ptr, is_const=is_const,
221 var_name=res.var_name, arr_len="datalen", arr_access="data", subty=res, is_native_primitive=False)
223 return TypeInfo(rust_obj=fn_arg.split(" ")[0], java_ty=res.java_ty + "[]", java_hu_ty=res.java_hu_ty + "[]",
224 java_fn_ty_arg="[" + res.java_fn_ty_arg, c_ty=consts.ptr_arr, passed_as_ptr=False, is_ptr=is_ptr,
225 nonnull_ptr=nonnull_ptr, is_const=is_const,
226 var_name=res.var_name, arr_len="datalen", arr_access="data", subty=res, is_native_primitive=False)
229 contains_trait = False
232 java_type_plural = None
233 if fn_arg.startswith("void"):
237 fn_arg = fn_arg[4:].strip()
239 elif fn_arg.startswith("bool"):
243 fn_arg = fn_arg[4:].strip()
245 elif fn_arg.startswith("uint8_t"):
246 mapped_type = consts.c_type_map['uint8_t']
247 java_ty = mapped_type[0]
250 fn_arg = fn_arg[7:].strip()
252 elif fn_arg.startswith("LDKu5"):
253 java_ty = consts.c_type_map['uint8_t'][0]
258 fn_arg = fn_arg[6:].strip()
259 elif fn_arg.startswith("uint16_t"):
260 mapped_type = consts.c_type_map['uint16_t']
261 java_ty = mapped_type[0]
264 fn_arg = fn_arg[8:].strip()
266 elif fn_arg.startswith("uint32_t"):
267 mapped_type = consts.c_type_map['uint32_t']
268 java_ty = mapped_type[0]
271 fn_arg = fn_arg[8:].strip()
273 elif fn_arg.startswith("uint64_t") or fn_arg.startswith("uintptr_t"):
274 # TODO: uintptr_t is arch-dependent :(
275 mapped_type = consts.c_type_map['uint64_t']
276 java_ty = mapped_type[0]
278 if fn_arg.startswith("uint64_t"):
280 fn_arg = fn_arg[8:].strip()
282 java_ty = consts.ptr_native_ty
284 rust_obj = "uintptr_t"
285 fn_arg = fn_arg[9:].strip()
287 elif is_const and fn_arg.startswith("char *"):
290 fn_ty_arg = "Ljava/lang/String;"
291 fn_arg = fn_arg[6:].strip()
292 elif fn_arg.startswith("LDKStr"):
296 fn_ty_arg = "Ljava/lang/String;"
297 fn_arg = fn_arg[6:].strip()
301 ma = var_ty_regex.match(fn_arg)
302 if ma.group(1).strip() in unitary_enums:
303 assert ma.group(1).strip().startswith("LDK")
304 java_ty = ma.group(1).strip()[3:]
306 c_ty = consts.result_c_ty
307 fn_ty_arg = "Lorg/ldk/enums/" + java_ty + ";"
308 fn_arg = ma.group(2).strip()
309 rust_obj = ma.group(1).strip()
311 c_ty = consts.ptr_c_ty
312 java_ty = consts.ptr_native_ty
313 java_hu_ty = ma.group(1).strip()
314 java_hu_ty = java_hu_ty.replace("LDKCOption", "Option")
315 java_hu_ty = java_hu_ty.replace("LDKCResult", "Result")
316 java_hu_ty = java_hu_ty.replace("LDKC2Tuple", "TwoTuple")
317 java_hu_ty = java_hu_ty.replace("LDKC3Tuple", "ThreeTuple")
318 java_hu_ty = java_hu_ty.replace("LDK", "")
320 fn_arg = ma.group(2).strip()
321 rust_obj = ma.group(1).strip()
322 if rust_obj in trait_structs:
323 contains_trait = True
324 elif rust_obj in complex_enums:
325 contains_trait = complex_enums[rust_obj]
328 if fn_arg.startswith(" *") or fn_arg.startswith("*"):
329 fn_arg = fn_arg.replace("*", "").strip()
331 c_ty = consts.ptr_c_ty
332 java_ty = consts.ptr_native_ty
335 var_is_arr = var_is_arr_regex.match(fn_arg)
336 if var_is_arr is not None or ret_arr_len is not None:
337 assert(not take_by_ptr)
339 # is there a special case for plurals?
340 if len(mapped_type) == 2:
341 java_ty = mapped_type[1]
343 java_ty = java_ty + "[]"
344 c_ty = c_ty + "Array"
345 if var_is_arr is not None:
346 if var_is_arr.group(1) == "":
347 return TypeInfo(rust_obj=rust_obj, java_ty=java_ty, java_hu_ty=java_ty, java_fn_ty_arg="[" + fn_ty_arg, c_ty=c_ty, is_const=is_const,
348 passed_as_ptr=False, is_ptr=False, nonnull_ptr=nonnull_ptr, var_name="arg",
349 arr_len=var_is_arr.group(2), arr_access=arr_access, is_native_primitive=False, contains_trait=contains_trait)
350 return TypeInfo(rust_obj=rust_obj, java_ty=java_ty, java_hu_ty=java_ty, java_fn_ty_arg="[" + fn_ty_arg, c_ty=c_ty, is_const=is_const,
351 passed_as_ptr=False, is_ptr=False, nonnull_ptr=nonnull_ptr, var_name=var_is_arr.group(1),
352 arr_len=var_is_arr.group(2), arr_access=arr_access, is_native_primitive=False, contains_trait=contains_trait)
354 if java_hu_ty is None:
356 return TypeInfo(rust_obj=rust_obj, java_ty=java_ty, java_hu_ty=java_hu_ty, java_fn_ty_arg=fn_ty_arg, c_ty=c_ty, passed_as_ptr=is_ptr or take_by_ptr,
357 is_const=is_const, is_ptr=is_ptr, nonnull_ptr=nonnull_ptr, var_name=fn_arg, arr_len=arr_len, arr_access=arr_access, is_native_primitive=is_primitive,
358 contains_trait=contains_trait)
360 fn_ptr_regex = re.compile("^extern const ([A-Za-z_0-9\* ]*) \(\*(.*)\)\((.*)\);$")
361 fn_ret_arr_regex = re.compile("(.*) \(\*(.*)\((.*)\)\)\[([0-9]*)\];$")
362 reg_fn_regex = re.compile("([A-Za-z_0-9\* ]* \*?)([a-zA-Z_0-9]*)\((.*)\);$")
366 from gen_type_mapping import TypeMappingGenerator
367 type_mapping_generator = TypeMappingGenerator(java_c_types, consts, opaque_structs, clone_fns, unitary_enums, trait_structs, complex_enums, result_types, tuple_types)
369 with open(sys.argv[1]) as in_h:
371 reg_fn = reg_fn_regex.match(line)
372 if reg_fn is not None:
373 if reg_fn.group(2).endswith("_clone"):
374 clone_fns.add(reg_fn.group(2))
376 rty = java_c_types(reg_fn.group(1), None)
377 if rty is not None and not rty.is_native_primitive and reg_fn.group(2) == rty.java_hu_ty + "_new":
378 constructor_fns[rty.rust_obj] = reg_fn.group(3)
380 arr_fn = fn_ret_arr_regex.match(line)
381 if arr_fn is not None:
382 if arr_fn.group(2).endswith("_clone"):
383 clone_fns.add(arr_fn.group(2))
384 # No object constructors return arrays, as then they wouldn't be an object constructor
387 # Define some manual clones...
388 clone_fns.add("ThirtyTwoBytes_clone")
389 write_c("static inline struct LDKThirtyTwoBytes ThirtyTwoBytes_clone(const struct LDKThirtyTwoBytes *orig) { struct LDKThirtyTwoBytes ret; memcpy(ret.data, orig->data, 32); return ret; }\n")
391 java_c_types_none_allowed = False # C structs created by cbindgen are declared in dependency order
393 with open(f"{sys.argv[3]}/structs/UtilMethods{consts.file_ext}", "a") as util:
394 util.write(consts.util_fn_pfx)
396 with open(sys.argv[1]) as in_h, open(f"{sys.argv[2]}/bindings{consts.file_ext}", "w") as out_java:
397 # Map a top-level function
398 def map_fn(line, re_match, ret_arr_len, c_call_string, doc_comment):
399 map_fn_with_ref_option(line, re_match, ret_arr_len, c_call_string, doc_comment, False)
400 def map_fn_with_ref_option(line, re_match, ret_arr_len, c_call_string, doc_comment, force_holds_ref):
401 method_return_type = re_match.group(1)
402 method_name = re_match.group(2)
403 method_comma_separated_arguments = re_match.group(3)
404 method_arguments = method_comma_separated_arguments.split(',')
406 if method_name.startswith("__"):
409 is_free = method_name.endswith("_free")
410 if method_name.startswith("COption") or method_name.startswith("CResult"):
411 struct_meth = method_name.rsplit("Z", 1)[0][1:] + "Z"
412 expected_struct = "LDKC" + struct_meth
413 struct_meth_name = method_name[len(struct_meth) + 1:].strip("_")
414 elif method_name.startswith("C2Tuple") or method_name.startswith("C3Tuple"):
415 tuple_name = method_name.rsplit("Z", 1)[0][2:] + "Z"
416 if method_name.startswith("C2Tuple"):
417 struct_meth = "Two" + tuple_name
418 expected_struct = "LDKC2" + tuple_name
420 struct_meth = "Three" + tuple_name
421 expected_struct = "LDKC3" + tuple_name
422 struct_meth_name = method_name[len(tuple_name) + 2:].strip("_")
424 struct_meth = method_name.split("_")[0]
425 expected_struct = "LDK" + struct_meth
426 struct_meth_name = method_name[len(struct_meth) + 1 if len(struct_meth) != 0 else 0:].strip("_")
428 (params_nullable, ret_nullable) = doc_to_params_ret_nullable(doc_comment)
430 return_type_info = type_mapping_generator.map_nullable_type(method_return_type.strip() + " ret", True, ret_arr_len, False, force_holds_ref)
432 return_type_info = type_mapping_generator.map_type(method_return_type.strip() + " ret", True, ret_arr_len, False, force_holds_ref)
434 if method_name.endswith("_clone") and expected_struct not in unitary_enums:
435 meth_line = "uintptr_t " + expected_struct.replace("LDK", "") + "_clone_ptr(" + expected_struct + " *NONNULL_PTR arg)"
436 write_c("static inline " + meth_line + " {\n")
437 write_c("\t" + return_type_info.ret_conv[0].replace("\n", "\n\t"))
438 write_c(method_name + "(arg)")
439 write_c(return_type_info.ret_conv[1])
440 write_c("\n\treturn " + return_type_info.ret_conv_name + ";\n}\n")
441 map_fn(meth_line + ";\n", re.compile("(uintptr_t) ([A-Za-z_0-9]*)\((.*)\)").match(meth_line), None, None, None)
444 default_constructor_args = {}
446 takes_self_ptr = False
449 for argument_index, argument in enumerate(method_arguments):
450 arg_ty = type_mapping_generator.java_c_types(argument, None)
451 argument_conversion_info = None
452 if argument_index == 0 and arg_ty.java_hu_ty == struct_meth:
453 argument_conversion_info = type_mapping_generator.map_type_with_info(arg_ty, False, None, is_free, True, False)
455 if argument_conversion_info.ty_info.is_ptr:
456 takes_self_ptr = True
457 elif arg_ty.var_name in params_nullable:
458 argument_conversion_info = type_mapping_generator.map_type_with_info(arg_ty, False, None, is_free, True, True)
459 if argument_conversion_info.arg_conv is not None and "Warning" in argument_conversion_info.arg_conv:
460 arg_ty_info = java_c_types(argument, None)
461 print("WARNING: Remapping argument " + arg_ty_info.var_name + " of function " + method_name + " to a reference")
462 print(" The argument appears to require a move, or not clonable, and is nullable.")
463 print(" Normally for arguments that require a move and are not clonable, we split")
464 print(" the argument into the type's constructor's arguments and just use those to")
465 print(" construct a new object on the fly.")
466 print(" However, because the object is nullable, doing so would mean we can no")
467 print(" longer allow the user to pass null, as we now have an argument list instead.")
468 print(" Thus, we blindly assume its really an Option<&Type> instead of an Option<Type>.")
469 print(" It may or may not actually be a reference, but its the simplest mapping option")
470 print(" and also the only use of this code today.")
471 arg_ty_info.requires_clone = False
472 argument_conversion_info = type_mapping_generator.map_type_with_info(arg_ty_info, False, None, is_free, True, True)
473 assert argument_conversion_info.nullable
474 assert argument_conversion_info.arg_conv is not None and "Warning" not in argument_conversion_info.arg_conv
476 argument_conversion_info = type_mapping_generator.map_type_with_info(arg_ty, False, None, is_free, True, False)
478 if argument_conversion_info.arg_conv is not None and "Warning" in argument_conversion_info.arg_conv:
479 if argument_conversion_info.rust_obj in constructor_fns:
481 for explode_arg in constructor_fns[argument_conversion_info.rust_obj].split(','):
482 explode_arg_conv = type_mapping_generator.map_type(explode_arg, False, None, False, True)
483 if explode_arg_conv.c_ty == "void":
484 # We actually want to handle this case, but for now its only used in NetGraphMsgHandler::new()
485 # which ends up resulting in a redundant constructor - both without arguments for the NetworkGraph.
488 if not argument_conversion_info.arg_name in default_constructor_args:
489 default_constructor_args[argument_conversion_info.arg_name] = []
490 default_constructor_args[argument_conversion_info.arg_name].append(explode_arg_conv)
491 argument_types.append(argument_conversion_info)
493 if not takes_self and return_type_info.java_hu_ty != struct_meth:
494 if not return_type_info.java_hu_ty.startswith("Result_" + struct_meth):
495 struct_meth_name = method_name
499 impl_on_struct = (expected_struct in opaque_structs or expected_struct in trait_structs or
500 expected_struct in complex_enums or expected_struct in complex_enums or
501 expected_struct in result_types or expected_struct in tuple_types) and not is_free
502 impl_on_utils = not impl_on_struct and (not is_free and not method_name.endswith("_clone") and
503 not method_name.startswith("TxOut") and
504 not method_name.startswith("_") and
505 method_name != "check_platform" and method_name != "Result_read" and
506 not expected_struct in unitary_enums and
507 ((not method_name.startswith("C2Tuple_") and not method_name.startswith("C3Tuple_"))
508 or method_name.endswith("_read")))
510 # If we're adding a static method, and it returns a primitive or an array of primitives,
511 # and a variable conversion adds a reference on the return type (via `this`), skip the
512 # variable's conversion reference-add (as we obviously cannot need a reference).
513 if impl_on_utils and (return_type_info.is_native_primitive or
514 (return_type_info.ty_info.subty is not None and return_type_info.ty_info.subty.is_native_primitive)):
515 for arg in argument_types:
516 if arg.from_hu_conv is not None and arg.from_hu_conv[1] != "":
517 if "this" in arg.from_hu_conv[1]:
518 arg.from_hu_conv = (arg.from_hu_conv[0], "")
520 out_java.write("\t// " + line)
521 (out_java_delta, out_c_delta, out_java_struct_delta) = \
522 consts.map_function(argument_types, c_call_string, method_name, struct_meth_name, return_type_info, struct_meth, default_constructor_args, takes_self, takes_self_ptr, args_known, type_mapping_generator, doc_comment)
523 out_java.write(out_java_delta)
526 assert len(argument_types) == 1
527 assert return_type_info.c_ty == "void"
528 write_c(consts.c_fn_ty_pfx + "void " + consts.c_fn_name_define_pfx(method_name, True) + argument_types[0].c_ty + " " + argument_types[0].ty_info.var_name + ") {\n")
529 if argument_types[0].ty_info.passed_as_ptr and not argument_types[0].ty_info.rust_obj in opaque_structs:
530 write_c("\tif ((" + argument_types[0].ty_info.var_name + " & 1) != 0) return;\n")
532 for info in argument_types:
533 if info.arg_conv is not None:
534 write_c("\t" + info.arg_conv.replace('\n', "\n\t") + "\n")
535 assert c_call_string is None
536 write_c("\t" + method_name + "(")
537 if argument_types[0].arg_conv_name is not None:
538 write_c(argument_types[0].arg_conv_name)
540 for info in argument_types:
541 if info.arg_conv_cleanup is not None:
542 write_c("\n\t" + info.arg_conv_cleanup.replace("\n", "\n\t"))
547 out_java_struct = None
549 out_java_struct = open(f"{sys.argv[3]}/structs/{struct_meth}{consts.file_ext}", "a")
550 out_java_struct.write(out_java_struct_delta)
552 out_java_struct = open(f"{sys.argv[3]}/structs/UtilMethods{consts.file_ext}", "a")
553 for line in out_java_struct_delta.splitlines():
554 out_java_struct.write(line + "\n")
556 def map_unitary_enum(struct_name, field_lines, enum_doc_comment):
557 assert struct_name.startswith("LDK")
558 with open(f"{sys.argv[3]}/enums/{struct_name[3:]}{consts.file_ext}", "w") as out_java_enum:
559 unitary_enums.add(struct_name)
560 for idx, (struct_line, _) in enumerate(field_lines):
562 assert(struct_line == "typedef enum %s {" % struct_name)
563 elif idx == len(field_lines) - 3:
564 assert(struct_line.endswith("_Sentinel,"))
565 elif idx == len(field_lines) - 2:
566 assert(struct_line == "} %s;" % struct_name)
567 elif idx == len(field_lines) - 1:
568 assert(struct_line == "")
569 assert struct_name.startswith("LDK")
570 (c_out, native_file_out, native_out) = consts.native_c_unitary_enum_map(struct_name[3:], [(x.strip().strip(","), y) for x, y in field_lines[1:-3]], enum_doc_comment)
572 out_java_enum.write(native_file_out)
573 out_java.write(native_out)
575 def map_complex_enum(struct_name, union_enum_items, inline_enum_variants, enum_doc_comment):
576 java_hu_type = struct_name.replace("LDK", "").replace("COption", "Option")
579 tag_field_lines = union_enum_items["field_lines"]
580 contains_trait = False
581 for idx, (struct_line, variant_docs) in enumerate(tag_field_lines):
583 assert(struct_line == "typedef enum %s_Tag {" % struct_name)
584 elif idx == len(tag_field_lines) - 3:
585 assert(struct_line.endswith("_Sentinel,"))
586 elif idx == len(tag_field_lines) - 2:
587 assert(struct_line == "} %s_Tag;" % struct_name)
588 elif idx == len(tag_field_lines) - 1:
589 assert(struct_line == "")
591 variant_name = struct_line.strip(' ,')[len(struct_name) + 1:]
593 if "LDK" + variant_name in union_enum_items:
594 enum_var_lines = union_enum_items["LDK" + variant_name]
595 for idx, (field, field_docs) in enumerate(enum_var_lines):
596 if idx != 0 and idx < len(enum_var_lines) - 2 and field.strip() != "":
597 field_ty = type_mapping_generator.java_c_types(field.strip(' ;'), None)
598 contains_trait |= field_ty.contains_trait
599 if field_docs is not None and doc_to_field_nullable(field_docs):
600 field_conv = type_mapping_generator.map_type_with_info(field_ty, False, None, False, True, True)
602 field_conv = type_mapping_generator.map_type_with_info(field_ty, False, None, False, True, False)
603 fields.append((field_conv, field_docs))
604 enum_variants.append(ComplexEnumVariantInfo(variant_name, variant_docs, fields, False))
605 elif camel_to_snake(variant_name) in inline_enum_variants:
606 # TODO: If we ever have a rust enum Variant(Option<Struct>) we need to pipe
607 # docs through to there, and then potentially mark the field nullable.
608 mapped = type_mapping_generator.map_type(inline_enum_variants[camel_to_snake(variant_name)] + " " + camel_to_snake(variant_name), False, None, False, True)
609 contains_trait |= mapped.ty_info.contains_trait
610 fields.append((mapped, None))
611 enum_variants.append(ComplexEnumVariantInfo(variant_name, variant_docs, fields, True))
613 enum_variants.append(ComplexEnumVariantInfo(variant_name, variant_docs, fields, True))
614 complex_enums[struct_name] = contains_trait
616 with open(f"{sys.argv[3]}/structs/{java_hu_type}{consts.file_ext}", "w") as out_java_enum:
617 (out_java_addendum, out_java_enum_addendum, out_c_addendum) = consts.map_complex_enum(struct_name, enum_variants, camel_to_snake, enum_doc_comment)
619 out_java_enum.write(out_java_enum_addendum)
620 out_java.write(out_java_addendum)
621 write_c(out_c_addendum)
623 def map_trait(struct_name, field_var_lines, trait_fn_lines, trait_doc_comment):
624 with open(f"{sys.argv[3]}/structs/{struct_name.replace('LDK', '')}{consts.file_ext}", "w") as out_java_trait:
626 flattened_field_var_convs = []
627 for var_line in field_var_lines:
628 if var_line.group(1) in trait_structs:
629 field_var_convs.append((var_line.group(1), var_line.group(2), trait_structs[var_line.group(1)]))
630 flattened_field_var_convs.append((var_line.group(1), var_line.group(2), ))
631 flattened_field_var_convs.extend(trait_structs[var_line.group(1)])
633 mapped = type_mapping_generator.map_type(var_line.group(1) + " " + var_line.group(2), False, None, False, False)
634 field_var_convs.append(mapped)
635 flattened_field_var_convs.append(mapped)
636 trait_structs[struct_name] = field_var_convs
639 for fn_docs, fn_line in trait_fn_lines:
640 if fn_line == "cloned":
641 ret_ty_info = type_mapping_generator.map_type("void", True, None, False, False)
642 field_fns.append(TraitMethInfo("cloned", False, ret_ty_info, [], fn_docs))
644 (nullable_params, ret_nullable) = doc_to_params_ret_nullable(fn_docs)
646 assert False # This isn't yet handled on the Java side
647 ret_ty_info.nullable = True
648 ret_ty_info = type_mapping_generator.map_nullable_type(fn_line.group(2).strip() + " ret", True, None, False, False)
650 ret_ty_info = type_mapping_generator.map_type(fn_line.group(2).strip() + " ret", True, None, False, False)
651 is_const = fn_line.group(4) is not None
654 for idx, arg in enumerate(fn_line.group(5).split(',')):
657 arg_ty_info = type_mapping_generator.java_c_types(arg, None)
658 if arg_ty_info.var_name in nullable_params:
659 # Types that are actually null instead of all-0s aren't yet handled on the Java side:
660 arg_conv_info = type_mapping_generator.map_type_with_info(arg_ty_info, True, None, False, False, True)
662 arg_conv_info = type_mapping_generator.map_type_with_info(arg_ty_info, True, None, False, False, False)
663 arg_tys.append(arg_conv_info)
664 field_fns.append(TraitMethInfo(fn_line.group(3), is_const, ret_ty_info, arg_tys, fn_docs))
666 (out_java_addendum, out_java_trait_addendum, out_c_addendum) = consts.native_c_map_trait(struct_name, field_var_convs, flattened_field_var_convs, field_fns, trait_doc_comment)
667 write_c(out_c_addendum)
668 out_java_trait.write(out_java_trait_addendum)
669 out_java.write(out_java_addendum)
671 for fn_docs, fn_line in trait_fn_lines:
672 if fn_line == "cloned":
674 # For now, just disable enabling the _call_log - we don't know how to inverse-map String
675 is_log = fn_line.group(3) == "log" and struct_name == "LDKLogger"
676 if fn_line.group(3) != "free" and fn_line.group(3) != "eq" and not is_log:
677 dummy_line = fn_line.group(2) + struct_name.replace("LDK", "") + "_" + fn_line.group(3) + " " + struct_name + " *NONNULL_PTR this_arg" + fn_line.group(5) + "\n"
678 map_fn(dummy_line, re.compile("([A-Za-z_0-9]*) *([A-Za-z_0-9]*) *(.*)").match(dummy_line), None, "(this_arg_conv->" + fn_line.group(3) + ")(this_arg_conv->this_arg", fn_docs)
679 for idx, var_line in enumerate(field_var_lines):
680 if var_line.group(1) not in trait_structs:
681 write_c(var_line.group(1) + " " + struct_name + "_set_get_" + var_line.group(2) + "(" + struct_name + "* this_arg) {\n")
682 write_c("\tif (this_arg->set_" + var_line.group(2) + " != NULL)\n")
683 write_c("\t\tthis_arg->set_" + var_line.group(2) + "(this_arg);\n")
684 write_c("\treturn this_arg->" + var_line.group(2) + ";\n")
686 dummy_line = var_line.group(1) + " " + struct_name.replace("LDK", "") + "_get_" + var_line.group(2) + " " + struct_name + " *NONNULL_PTR this_arg" + fn_line.group(5) + "\n"
687 map_fn(dummy_line, re.compile("([A-Za-z_0-9]*) *([A-Za-z_0-9]*) *(.*)").match(dummy_line), None, struct_name + "_set_get_" + var_line.group(2) + "(this_arg_conv", fn_docs)
689 def map_result(struct_name, res_ty, err_ty):
690 result_types.add(struct_name)
691 human_ty = struct_name.replace("LDKCResult", "Result")
692 res_map = type_mapping_generator.map_type(res_ty + " res", True, None, False, True)
693 err_map = type_mapping_generator.map_type(err_ty + " err", True, None, False, True)
694 java_hu_struct = consts.map_result(struct_name, res_map, err_map)
695 create_getter(struct_name, res_ty, "ok", ("*", "->contents.result"), ("", "->result_ok"))
696 create_getter(struct_name, err_ty, "err", ("*", "->contents.err"), ("!", "->result_ok"))
697 with open(f"{sys.argv[3]}/structs/{human_ty}{consts.file_ext}", "w") as out_java_struct:
698 out_java_struct.write(java_hu_struct)
700 def create_getter(struct_name, field_decl, field_name, accessor, check_sfx):
701 field_ty = java_c_types(field_decl + " " + field_name, None)
702 ptr_fn_defn = field_decl + " *" + struct_name.replace("LDK", "") + "_get_" + field_name + "(" + struct_name + " *NONNULL_PTR owner)"
703 owned_fn_defn = field_decl + " " + struct_name.replace("LDK", "") + "_get_" + field_name + "(" + struct_name + " *NONNULL_PTR owner)"
706 if field_ty.rust_obj is not None and field_ty.rust_obj.replace("LDK", "") + "_clone" in clone_fns:
707 fn_defn = owned_fn_defn
708 write_c("static inline " + fn_defn + "{\n")
709 if check_sfx is not None:
710 write_c("CHECK(" + check_sfx[0] + "owner" + check_sfx[1] + ");\n")
711 write_c("\treturn " + field_ty.rust_obj.replace("LDK", "") + "_clone(&" + accessor[0] + "owner" + accessor[1] + ");\n")
712 elif field_ty.arr_len is not None or field_ty.is_native_primitive or field_ty.rust_obj in unitary_enums:
713 fn_defn = owned_fn_defn
714 write_c("static inline " + fn_defn + "{\n")
715 if check_sfx is not None:
716 write_c("CHECK(" + check_sfx[0] + "owner" + check_sfx[1] + ");\n")
717 write_c("\treturn " + accessor[0] + "owner" + accessor[1] + ";\n")
720 fn_defn = ptr_fn_defn
721 write_c("static inline " + fn_defn + "{\n")
722 if check_sfx is not None:
723 write_c("CHECK(" + check_sfx[0] + "owner" + check_sfx[1] + ");\n")
724 write_c("\treturn &" + accessor[0] + "owner" + accessor[1] + ";\n")
727 dummy_line = fn_defn + ";\n"
728 map_fn_with_ref_option(dummy_line, reg_fn_regex.match(dummy_line), None, None, "", holds_ref)
730 def map_tuple(struct_name, field_lines):
731 human_ty = struct_name.replace("LDKC2Tuple", "TwoTuple").replace("LDKC3Tuple", "ThreeTuple")
732 with open(f"{sys.argv[3]}/structs/{human_ty}{consts.file_ext}", "w") as out_java_struct:
733 out_java_struct.write(consts.map_tuple(struct_name))
735 for idx, (line, _) in enumerate(field_lines):
736 if idx != 0 and idx < len(field_lines) - 2:
737 ty_list.append(java_c_types(line.strip(';'), None))
738 tuple_types[struct_name] = (ty_list, struct_name)
740 # Map virtual getter functions
741 for idx, (line, _) in enumerate(field_lines):
742 if idx != 0 and idx < len(field_lines) - 2:
743 field_name = chr(ord('a') + idx - 1)
744 assert line.endswith(" " + field_name + ";")
745 create_getter(struct_name, line[:-3].strip(), field_name, ("", "->" + field_name), None)
747 out_java.write(consts.bindings_header)
748 with open(f"{sys.argv[2]}/version{consts.file_ext}", "w") as out_java_version:
749 out_java_version.write(consts.bindings_version_file.replace('<git_version_ldk_garbagecollected>', local_git_version))
751 with open(f"{sys.argv[3]}/structs/CommonBase{consts.file_ext}", "w") as out_java_struct:
752 out_java_struct.write(consts.common_base)
755 last_block_comment = None
758 const_val_regex = re.compile("^extern const ([A-Za-z_0-9]*) ([A-Za-z_0-9]*);$")
760 line_indicates_result_regex = re.compile("^ union (LDKCResult_[A-Za-z_0-9]*Ptr) contents;$")
761 line_indicates_vec_regex = re.compile("^ (struct |enum |union )?([A-Za-z_0-9]*) \*data;$")
762 line_indicates_opaque_regex = re.compile("^ bool is_owned;$")
763 line_indicates_trait_regex = re.compile("^ (struct |enum |union )?([A-Za-z_0-9]* \*?)\(\*([A-Za-z_0-9]*)\)\((const )?void \*this_arg(.*)\);$")
764 assert(line_indicates_trait_regex.match(" uintptr_t (*send_data)(void *this_arg, LDKu8slice data, bool resume_read);"))
765 assert(line_indicates_trait_regex.match(" struct LDKCVec_MessageSendEventZ (*get_and_clear_pending_msg_events)(const void *this_arg);"))
766 assert(line_indicates_trait_regex.match(" struct LDKCVec_u8Z (*write)(const void *this_arg);"))
767 line_indicates_trait_clone_regex = re.compile("^ void \(\*cloned\)\(struct ([A-Za-z0-9])* \*NONNULL_PTR new_[A-Za-z0-9]*\);$")
768 assert(line_indicates_trait_clone_regex.match(" void (*cloned)(struct LDKSign *NONNULL_PTR new_Sign);"))
769 line_field_var_regex = re.compile("^ struct ([A-Za-z_0-9]*) ([A-Za-z_0-9]*);$")
770 assert(line_field_var_regex.match(" struct LDKMessageSendEventsProvider MessageSendEventsProvider;"))
771 assert(line_field_var_regex.match(" struct LDKChannelPublicKeys pubkeys;"))
772 struct_name_regex = re.compile("^typedef (struct|enum|union) (MUST_USE_STRUCT )?(LDK[A-Za-z_0-9]*) {$")
773 assert(struct_name_regex.match("typedef struct LDKCVec_u8Z {"))
774 assert(struct_name_regex.match("typedef enum LDKNetwork {"))
776 union_enum_items = {}
777 result_ptr_struct_items = {}
779 if block_comment is not None:
780 if line.endswith("*/\n"):
781 last_block_comment = block_comment.strip("\n")
784 block_comment = block_comment + line.strip(" /*")
785 elif cur_block_obj is not None:
786 cur_block_obj = cur_block_obj + line
787 if line.startswith("} "):
791 obj_lines = cur_block_obj.split("\n")
793 result_contents = None
794 is_unitary_enum = False
795 is_union_enum = False
800 last_struct_block_comment = None
802 for idx, struct_line in enumerate(obj_lines):
803 if struct_line.strip().startswith("/*"):
804 block_comment = struct_line.strip(" /*")
805 if block_comment is not None:
806 if struct_line.endswith("*/"):
807 last_struct_block_comment = block_comment.strip("\n")
810 block_comment = block_comment + "\n" + struct_line.strip(" /*").replace("…", "...")
812 struct_name_match = struct_name_regex.match(struct_line)
813 if struct_name_match is not None:
814 struct_name = struct_name_match.group(3)
815 if struct_name_match.group(1) == "enum":
816 if not struct_name.endswith("_Tag"):
817 is_unitary_enum = True
820 elif struct_name_match.group(1) == "union":
822 if line_indicates_opaque_regex.match(struct_line):
824 result_match = line_indicates_result_regex.match(struct_line)
825 if result_match is not None:
826 result_contents = result_match.group(1)
827 vec_ty_match = line_indicates_vec_regex.match(struct_line)
828 if vec_ty_match is not None and struct_name.startswith("LDKCVec_"):
829 vec_ty = vec_ty_match.group(2)
830 elif struct_name.startswith("LDKC2Tuple_") or struct_name.startswith("LDKC3Tuple_"):
832 trait_fn_match = line_indicates_trait_regex.match(struct_line)
833 if trait_fn_match is not None:
834 trait_fn_lines.append((last_struct_block_comment, trait_fn_match))
835 trait_clone_fn_match = line_indicates_trait_clone_regex.match(struct_line)
836 if trait_clone_fn_match is not None:
837 trait_fn_lines.append((last_struct_block_comment, "cloned"))
838 field_var_match = line_field_var_regex.match(struct_line)
839 if field_var_match is not None:
840 field_var_lines.append(field_var_match)
841 field_lines.append((struct_line, last_struct_block_comment))
842 last_struct_block_comment = None
844 assert(struct_name is not None)
845 assert(len(trait_fn_lines) == 0 or not (is_opaque or is_unitary_enum or is_union_enum or is_union or result_contents is not None or vec_ty is not None))
846 assert(not is_opaque or not (len(trait_fn_lines) != 0 or is_unitary_enum or is_union_enum or is_union or result_contents is not None or vec_ty is not None))
847 assert(not is_unitary_enum or not (len(trait_fn_lines) != 0 or is_opaque or is_union_enum or is_union or result_contents is not None or vec_ty is not None))
848 assert(not is_union_enum or not (len(trait_fn_lines) != 0 or is_unitary_enum or is_opaque or is_union or result_contents is not None or vec_ty is not None))
849 assert(not is_union or not (len(trait_fn_lines) != 0 or is_unitary_enum or is_union_enum or is_opaque or result_contents is not None or vec_ty is not None))
850 assert(result_contents is None or not (len(trait_fn_lines) != 0 or is_unitary_enum or is_union_enum or is_opaque or is_union or vec_ty is not None))
851 assert(vec_ty is None or not (len(trait_fn_lines) != 0 or is_unitary_enum or is_union_enum or is_opaque or is_union or result_contents is not None))
854 opaque_structs.add(struct_name)
855 with open(f"{sys.argv[3]}/structs/{struct_name.replace('LDK', '')}{consts.file_ext}", "w") as out_java_struct:
856 out_opaque_struct_human = consts.map_opaque_struct(struct_name, last_block_comment)
857 last_block_comment = None
858 out_java_struct.write(out_opaque_struct_human)
859 elif result_contents is not None:
860 assert result_contents in result_ptr_struct_items
861 res_ty, err_ty = result_ptr_struct_items[result_contents]
862 map_result(struct_name, res_ty, err_ty)
863 elif struct_name.startswith("LDKCResult_") and struct_name.endswith("ZPtr"):
864 for line, _ in field_lines:
865 if line.endswith("*result;"):
866 res_ty = line[:-8].strip()
867 elif line.endswith("*err;"):
868 err_ty = line[:-5].strip()
869 result_ptr_struct_items[struct_name] = (res_ty, err_ty)
870 result_types.add(struct_name[:-3])
872 map_tuple(struct_name, field_lines)
873 elif vec_ty is not None:
874 ty_info = type_mapping_generator.map_type(vec_ty + " arr_elem", False, None, False, False)
875 if ty_info.is_native_primitive:
876 clone_fns.add(struct_name.replace("LDK", "") + "_clone")
877 write_c("static inline " + struct_name + " " + struct_name.replace("LDK", "") + "_clone(const " + struct_name + " *orig) {\n")
878 write_c("\t" + struct_name + " ret = { .data = MALLOC(sizeof(" + ty_info.c_ty + ") * orig->datalen, \"" + struct_name + " clone bytes\"), .datalen = orig->datalen };\n")
879 write_c("\tmemcpy(ret.data, orig->data, sizeof(" + ty_info.c_ty + ") * ret.datalen);\n")
880 write_c("\treturn ret;\n}\n")
881 elif (ty_info.rust_obj.replace("LDK", "") + "_clone") in clone_fns:
882 ty_name = "CVec_" + ty_info.rust_obj.replace("LDK", "") + "Z";
883 clone_fns.add(ty_name + "_clone")
884 write_c("static inline " + struct_name + " " + ty_name + "_clone(const " + struct_name + " *orig) {\n")
885 write_c("\t" + struct_name + " ret = { .data = MALLOC(sizeof(" + ty_info.rust_obj + ") * orig->datalen, \"" + struct_name + " clone bytes\"), .datalen = orig->datalen };\n")
886 write_c("\tfor (size_t i = 0; i < ret.datalen; i++) {\n")
887 write_c("\t\tret.data[i] = " + ty_info.rust_obj.replace("LDK", "") + "_clone(&orig->data[i]);\n")
888 write_c("\t}\n\treturn ret;\n}\n")
890 assert(struct_name.endswith("_Tag"))
891 struct_name = struct_name[:-4]
892 union_enum_items[struct_name] = {"field_lines": field_lines}
893 elif struct_name.endswith("_Body") and struct_name.split("_")[0] in union_enum_items:
894 enum_var_name = struct_name.split("_")
895 union_enum_items[enum_var_name[0]][enum_var_name[1]] = field_lines
896 elif struct_name in union_enum_items:
899 for line, _ in field_lines:
900 if line == " struct {":
904 elif elem_items > -1:
906 if line.startswith("struct "):
908 elif line.startswith("enum "):
910 split = line.split(" ")
911 assert len(split) == 2
912 tuple_variants[split[1].strip(";")] = split[0]
915 # We don't currently support tuple variant with more than one element
917 map_complex_enum(struct_name, union_enum_items[struct_name], tuple_variants, last_block_comment)
918 last_block_comment = None
919 elif is_unitary_enum:
920 map_unitary_enum(struct_name, field_lines, last_block_comment)
921 last_block_comment = None
922 elif len(trait_fn_lines) > 0:
923 map_trait(struct_name, field_var_lines, trait_fn_lines, last_block_comment)
924 elif struct_name == "LDKTxOut":
925 with open(f"{sys.argv[3]}/structs/TxOut{consts.file_ext}", "w") as out_java_struct:
926 out_java_struct.write(consts.hu_struct_file_prefix)
927 out_java_struct.write(consts.txout_defn)
928 fn_line = "struct LDKCVec_u8Z TxOut_get_script_pubkey (struct LDKTxOut* thing)"
929 write_c(fn_line + " {")
930 write_c("\treturn CVec_u8Z_clone(&thing->script_pubkey);")
932 map_fn(fn_line + "\n", re.compile("(.*) (TxOut_get_script_pubkey) \((.*)\)").match(fn_line), None, None, None)
933 fn_line = "uint64_t TxOut_get_value (struct LDKTxOut* thing)"
934 write_c(fn_line + " {")
935 write_c("\treturn thing->value;")
937 map_fn(fn_line + "\n", re.compile("(.*) (TxOut_get_value) \((.*)\)").match(fn_line), None, None, None)
939 pass # Everything remaining is a byte[] or some form
942 fn_ptr = fn_ptr_regex.match(line)
943 fn_ret_arr = fn_ret_arr_regex.match(line)
944 reg_fn = reg_fn_regex.match(line)
945 const_val = const_val_regex.match(line)
947 if line.startswith("#include <"):
949 elif line.startswith("/*"):
950 if not line.endswith("*/\n"):
951 block_comment = line.strip(" /*")
952 elif line.startswith("typedef enum "):
954 elif line.startswith("typedef struct "):
956 elif line.startswith("typedef union "):
958 elif fn_ptr is not None:
959 map_fn(line, fn_ptr, None, None, last_block_comment)
960 last_block_comment = None
961 elif fn_ret_arr is not None:
962 map_fn(line, fn_ret_arr, fn_ret_arr.group(4), None, last_block_comment)
963 last_block_comment = None
964 elif reg_fn is not None:
965 map_fn(line, reg_fn, None, None, last_block_comment)
966 last_block_comment = None
967 elif const_val_regex is not None:
968 # TODO Map const variables
973 out_java.write(consts.bindings_footer)
974 for struct_name in opaque_structs:
975 with open(f"{sys.argv[3]}/structs/{struct_name.replace('LDK', '')}{consts.file_ext}", "a") as out_java_struct:
976 out_java_struct.write("}\n")
977 for struct_name in trait_structs:
978 with open(f"{sys.argv[3]}/structs/{struct_name.replace('LDK', '')}{consts.file_ext}", "a") as out_java_struct:
979 out_java_struct.write("}\n")
980 for struct_name in complex_enums:
981 with open(f"{sys.argv[3]}/structs/{struct_name.replace('LDK', '').replace('COption', 'Option')}{consts.file_ext}", "a") as out_java_struct:
982 out_java_struct.write("}\n")
983 for struct_name in result_types:
984 with open(f"{sys.argv[3]}/structs/{struct_name.replace('LDKCResult', 'Result')}{consts.file_ext}", "a") as out_java_struct:
985 out_java_struct.write("}\n")
986 for struct_name in tuple_types:
987 struct_hu_name = struct_name.replace("LDKC2Tuple", "TwoTuple").replace("LDKC3Tuple", "ThreeTuple")
988 with open(f"{sys.argv[3]}/structs/{struct_hu_name}{consts.file_ext}", "a") as out_java_struct:
989 out_java_struct.write("}\n")
991 with open(f"{sys.argv[4]}/bindings.c.body", "w") as out_c:
992 out_c.write(consts.c_file_pfx)
993 out_c.write(consts.init_str())
995 with open(f"{sys.argv[4]}/version.c", "w") as out_c:
996 out_c.write(consts.c_version_file.replace('<git_version_ldk_garbagecollected>', local_git_version))
997 with open(f"{sys.argv[3]}/structs/UtilMethods{consts.file_ext}", "a") as util:
998 util.write(consts.util_fn_sfx)