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
234 if fn_arg.startswith("void"):
238 fn_arg = fn_arg[4:].strip()
240 elif fn_arg.startswith("bool"):
245 fn_arg = fn_arg[4:].strip()
247 elif fn_arg.startswith("uint8_t"):
248 mapped_type = consts.c_type_map['uint8_t']
249 java_ty = mapped_type[0]
253 fn_arg = fn_arg[7:].strip()
255 elif fn_arg.startswith("LDKu5"):
256 java_ty = consts.c_type_map['uint8_t'][0]
262 fn_arg = fn_arg[6:].strip()
263 elif fn_arg.startswith("uint16_t"):
264 mapped_type = consts.c_type_map['uint16_t']
265 java_ty = mapped_type[0]
269 fn_arg = fn_arg[8:].strip()
271 elif fn_arg.startswith("uint32_t"):
272 mapped_type = consts.c_type_map['uint32_t']
273 java_ty = mapped_type[0]
277 fn_arg = fn_arg[8:].strip()
279 elif fn_arg.startswith("uint64_t") or fn_arg.startswith("uintptr_t"):
280 # TODO: uintptr_t is arch-dependent :(
281 mapped_type = consts.c_type_map['uint64_t']
282 java_ty = mapped_type[0]
284 if fn_arg.startswith("uint64_t"):
287 fn_arg = fn_arg[8:].strip()
289 java_ty = consts.ptr_native_ty
292 rust_obj = "uintptr_t"
293 fn_arg = fn_arg[9:].strip()
295 elif is_const and fn_arg.startswith("char *"):
296 java_ty = consts.java_type_map["String"]
297 java_hu_ty = consts.java_hu_type_map["String"]
300 fn_ty_arg = "Ljava/lang/String;"
301 fn_arg = fn_arg[6:].strip()
302 elif fn_arg.startswith("LDKStr"):
305 java_ty = consts.java_type_map["String"]
306 java_hu_ty = consts.java_hu_type_map["String"]
308 fn_ty_arg = "Ljava/lang/String;"
309 fn_arg = fn_arg[6:].strip()
313 ma = var_ty_regex.match(fn_arg)
314 arr_ty = ma.group(1).strip()
315 if ma.group(1).strip() in unitary_enums:
316 assert ma.group(1).strip().startswith("LDK")
317 java_ty = ma.group(1).strip()[3:]
319 c_ty = consts.result_c_ty
320 fn_ty_arg = "Lorg/ldk/enums/" + java_ty + ";"
321 fn_arg = ma.group(2).strip()
322 rust_obj = ma.group(1).strip()
324 c_ty = consts.ptr_c_ty
325 java_ty = consts.ptr_native_ty
326 java_hu_ty = ma.group(1).strip()
327 java_hu_ty = java_hu_ty.replace("LDKCOption", "Option")
328 java_hu_ty = java_hu_ty.replace("LDKCResult", "Result")
329 java_hu_ty = java_hu_ty.replace("LDKC2Tuple", "TwoTuple")
330 java_hu_ty = java_hu_ty.replace("LDKC3Tuple", "ThreeTuple")
331 java_hu_ty = java_hu_ty.replace("LDK", "")
333 fn_arg = ma.group(2).strip()
334 rust_obj = ma.group(1).strip()
335 if rust_obj in trait_structs:
336 contains_trait = True
337 elif rust_obj in complex_enums:
338 contains_trait = complex_enums[rust_obj]
341 if fn_arg.startswith(" *") or fn_arg.startswith("*"):
342 fn_arg = fn_arg.replace("*", "").strip()
344 c_ty = consts.ptr_c_ty
345 java_ty = consts.ptr_native_ty
348 var_is_arr = var_is_arr_regex.match(fn_arg)
350 if var_is_arr is not None or ret_arr_len is not None:
351 assert(not take_by_ptr)
353 # is there a special case for plurals?
354 if len(mapped_type) == 2:
355 java_ty = mapped_type[1]
357 java_ty = java_ty + "[]"
358 c_ty = c_ty + "Array"
360 subty = java_c_types(arr_ty, None)
362 assert java_c_types_none_allowed
365 subty.pass_by_ref = True
367 if var_is_arr is not None:
368 if var_is_arr.group(1) == "":
369 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,
370 passed_as_ptr=False, is_ptr=False, nonnull_ptr=nonnull_ptr, var_name="arg", subty=subty,
371 arr_len=var_is_arr.group(2), arr_access=arr_access, is_native_primitive=False, contains_trait=contains_trait)
372 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,
373 passed_as_ptr=False, is_ptr=False, nonnull_ptr=nonnull_ptr, var_name=var_is_arr.group(1), subty=subty,
374 arr_len=var_is_arr.group(2), arr_access=arr_access, is_native_primitive=False, contains_trait=contains_trait)
376 if java_hu_ty is None:
378 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,
379 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,
380 contains_trait=contains_trait, subty=subty)
382 fn_ptr_regex = re.compile("^extern const ([A-Za-z_0-9\* ]*) \(\*(.*)\)\((.*)\);$")
383 fn_ret_arr_regex = re.compile("(.*) \(\*(.*)\((.*)\)\)\[([0-9]*)\];$")
384 reg_fn_regex = re.compile("([A-Za-z_0-9\* ]* \*?)([a-zA-Z_0-9]*)\((.*)\);$")
388 from gen_type_mapping import TypeMappingGenerator
389 type_mapping_generator = TypeMappingGenerator(java_c_types, consts, opaque_structs, clone_fns, unitary_enums, trait_structs, complex_enums, result_types, tuple_types)
391 with open(sys.argv[1]) as in_h:
393 reg_fn = reg_fn_regex.match(line)
394 if reg_fn is not None:
395 if reg_fn.group(2).endswith("_clone"):
396 clone_fns.add(reg_fn.group(2))
398 rty = java_c_types(reg_fn.group(1), None)
399 if rty is not None and not rty.is_native_primitive and reg_fn.group(2) == rty.java_hu_ty + "_new":
400 constructor_fns[rty.rust_obj] = reg_fn.group(3)
402 arr_fn = fn_ret_arr_regex.match(line)
403 if arr_fn is not None:
404 if arr_fn.group(2).endswith("_clone"):
405 clone_fns.add(arr_fn.group(2))
406 # No object constructors return arrays, as then they wouldn't be an object constructor
409 # Define some manual clones...
410 clone_fns.add("ThirtyTwoBytes_clone")
411 write_c("static inline struct LDKThirtyTwoBytes ThirtyTwoBytes_clone(const struct LDKThirtyTwoBytes *orig) { struct LDKThirtyTwoBytes ret; memcpy(ret.data, orig->data, 32); return ret; }\n")
413 java_c_types_none_allowed = False # C structs created by cbindgen are declared in dependency order
415 with open(f"{sys.argv[3]}/structs/UtilMethods{consts.file_ext}", "a") as util:
416 util.write(consts.util_fn_pfx)
418 with open(sys.argv[1]) as in_h, open(f"{sys.argv[2]}/bindings{consts.file_ext}", "w") as out_java:
419 # Map a top-level function
420 def map_fn(line, re_match, ret_arr_len, c_call_string, doc_comment):
421 map_fn_with_ref_option(line, re_match, ret_arr_len, c_call_string, doc_comment, False)
422 def map_fn_with_ref_option(line, re_match, ret_arr_len, c_call_string, doc_comment, force_holds_ref):
423 method_return_type = re_match.group(1)
424 method_name = re_match.group(2)
425 method_comma_separated_arguments = re_match.group(3)
426 method_arguments = method_comma_separated_arguments.split(',')
428 if method_name.startswith("__"):
431 is_free = method_name.endswith("_free")
432 if method_name.startswith("COption") or method_name.startswith("CResult"):
433 struct_meth = method_name.rsplit("Z", 1)[0][1:] + "Z"
434 expected_struct = "LDKC" + struct_meth
435 struct_meth_name = method_name[len(struct_meth) + 1:].strip("_")
436 elif method_name.startswith("C2Tuple") or method_name.startswith("C3Tuple"):
437 tuple_name = method_name.rsplit("Z", 1)[0][2:] + "Z"
438 if method_name.startswith("C2Tuple"):
439 struct_meth = "Two" + tuple_name
440 expected_struct = "LDKC2" + tuple_name
442 struct_meth = "Three" + tuple_name
443 expected_struct = "LDKC3" + tuple_name
444 struct_meth_name = method_name[len(tuple_name) + 2:].strip("_")
446 struct_meth = method_name.split("_")[0]
447 expected_struct = "LDK" + struct_meth
448 struct_meth_name = method_name[len(struct_meth) + 1 if len(struct_meth) != 0 else 0:].strip("_")
450 (params_nullable, ret_nullable) = doc_to_params_ret_nullable(doc_comment)
452 return_type_info = type_mapping_generator.map_nullable_type(method_return_type.strip() + " ret", True, ret_arr_len, False, force_holds_ref)
454 return_type_info = type_mapping_generator.map_type(method_return_type.strip() + " ret", True, ret_arr_len, False, force_holds_ref)
456 if method_name.endswith("_clone") and expected_struct not in unitary_enums:
457 meth_line = "uintptr_t " + expected_struct.replace("LDK", "") + "_clone_ptr(" + expected_struct + " *NONNULL_PTR arg)"
458 write_c("static inline " + meth_line + " {\n")
459 write_c("\t" + return_type_info.ret_conv[0].replace("\n", "\n\t"))
460 write_c(method_name + "(arg)")
461 write_c(return_type_info.ret_conv[1])
462 write_c("\n\treturn " + return_type_info.ret_conv_name + ";\n}\n")
463 map_fn(meth_line + ";\n", re.compile("(uintptr_t) ([A-Za-z_0-9]*)\((.*)\)").match(meth_line), None, None, None)
466 default_constructor_args = {}
468 takes_self_ptr = False
471 for argument_index, argument in enumerate(method_arguments):
472 arg_ty = type_mapping_generator.java_c_types(argument, None)
473 argument_conversion_info = None
474 if argument_index == 0 and arg_ty.java_hu_ty == struct_meth:
475 argument_conversion_info = type_mapping_generator.map_type_with_info(arg_ty, False, None, is_free, True, False)
477 if argument_conversion_info.ty_info.is_ptr:
478 takes_self_ptr = True
479 elif arg_ty.var_name in params_nullable:
480 argument_conversion_info = type_mapping_generator.map_type_with_info(arg_ty, False, None, is_free, True, True)
481 if argument_conversion_info.arg_conv is not None and "Warning" in argument_conversion_info.arg_conv:
482 arg_ty_info = java_c_types(argument, None)
483 print("WARNING: Remapping argument " + arg_ty_info.var_name + " of function " + method_name + " to a reference")
484 print(" The argument appears to require a move, or not clonable, and is nullable.")
485 print(" Normally for arguments that require a move and are not clonable, we split")
486 print(" the argument into the type's constructor's arguments and just use those to")
487 print(" construct a new object on the fly.")
488 print(" However, because the object is nullable, doing so would mean we can no")
489 print(" longer allow the user to pass null, as we now have an argument list instead.")
490 print(" Thus, we blindly assume its really an Option<&Type> instead of an Option<Type>.")
491 print(" It may or may not actually be a reference, but its the simplest mapping option")
492 print(" and also the only use of this code today.")
493 arg_ty_info.requires_clone = False
494 argument_conversion_info = type_mapping_generator.map_type_with_info(arg_ty_info, False, None, is_free, True, True)
495 assert argument_conversion_info.nullable
496 assert argument_conversion_info.arg_conv is not None and "Warning" not in argument_conversion_info.arg_conv
498 argument_conversion_info = type_mapping_generator.map_type_with_info(arg_ty, False, None, is_free, True, False)
500 if argument_conversion_info.arg_conv is not None and "Warning" in argument_conversion_info.arg_conv:
501 if argument_conversion_info.rust_obj in constructor_fns:
503 for explode_arg in constructor_fns[argument_conversion_info.rust_obj].split(','):
504 explode_arg_conv = type_mapping_generator.map_type(explode_arg, False, None, False, True)
505 if explode_arg_conv.c_ty == "void":
506 # We actually want to handle this case, but for now its only used in NetGraphMsgHandler::new()
507 # which ends up resulting in a redundant constructor - both without arguments for the NetworkGraph.
510 if not argument_conversion_info.arg_name in default_constructor_args:
511 default_constructor_args[argument_conversion_info.arg_name] = []
512 default_constructor_args[argument_conversion_info.arg_name].append(explode_arg_conv)
513 argument_types.append(argument_conversion_info)
515 if not takes_self and return_type_info.java_hu_ty != struct_meth:
516 if not return_type_info.java_hu_ty.startswith("Result_" + struct_meth):
517 struct_meth_name = method_name
521 impl_on_struct = (expected_struct in opaque_structs or expected_struct in trait_structs or
522 expected_struct in complex_enums or expected_struct in complex_enums or
523 expected_struct in result_types or expected_struct in tuple_types) and not is_free
524 impl_on_utils = not impl_on_struct and (not is_free and not method_name.endswith("_clone") and
525 not method_name.startswith("TxOut") and
526 not method_name.startswith("_") and
527 method_name != "check_platform" and method_name != "Result_read" and
528 not expected_struct in unitary_enums and
529 ((not method_name.startswith("C2Tuple_") and not method_name.startswith("C3Tuple_"))
530 or method_name.endswith("_read")))
532 # If we're adding a static method, and it returns a primitive or an array of primitives,
533 # and a variable conversion adds a reference on the return type (via `this`), skip the
534 # variable's conversion reference-add (as we obviously cannot need a reference).
535 if impl_on_utils and (return_type_info.is_native_primitive or
536 (return_type_info.ty_info.subty is not None and return_type_info.ty_info.subty.is_native_primitive)):
537 for arg in argument_types:
538 if arg.from_hu_conv is not None and arg.from_hu_conv[1] != "":
539 if "this" in arg.from_hu_conv[1]:
540 arg.from_hu_conv = (arg.from_hu_conv[0], "")
542 out_java.write("\t// " + line)
543 (out_java_delta, out_c_delta, out_java_struct_delta) = \
544 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)
545 out_java.write(out_java_delta)
548 assert len(argument_types) == 1
549 assert return_type_info.c_ty == "void"
550 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")
551 if argument_types[0].ty_info.passed_as_ptr and not argument_types[0].ty_info.rust_obj in opaque_structs:
552 write_c("\tif ((" + argument_types[0].ty_info.var_name + " & 1) != 0) return;\n")
554 for info in argument_types:
555 if info.arg_conv is not None:
556 write_c("\t" + info.arg_conv.replace('\n', "\n\t") + "\n")
557 assert c_call_string is None
558 write_c("\t" + method_name + "(")
559 if argument_types[0].arg_conv_name is not None:
560 write_c(argument_types[0].arg_conv_name)
562 for info in argument_types:
563 if info.arg_conv_cleanup is not None:
564 write_c("\n\t" + info.arg_conv_cleanup.replace("\n", "\n\t"))
569 out_java_struct = None
571 out_java_struct = open(f"{sys.argv[3]}/structs/{struct_meth}{consts.file_ext}", "a")
572 out_java_struct.write(out_java_struct_delta)
574 out_java_struct = open(f"{sys.argv[3]}/structs/UtilMethods{consts.file_ext}", "a")
575 for line in out_java_struct_delta.splitlines():
576 out_java_struct.write(line + "\n")
578 def map_unitary_enum(struct_name, field_lines, enum_doc_comment):
579 assert struct_name.startswith("LDK")
580 with open(f"{sys.argv[3]}/enums/{struct_name[3:]}{consts.file_ext}", "w") as out_java_enum:
581 unitary_enums.add(struct_name)
582 for idx, (struct_line, _) in enumerate(field_lines):
584 assert(struct_line == "typedef enum %s {" % struct_name)
585 elif idx == len(field_lines) - 3:
586 assert(struct_line.endswith("_Sentinel,"))
587 elif idx == len(field_lines) - 2:
588 assert(struct_line == "} %s;" % struct_name)
589 elif idx == len(field_lines) - 1:
590 assert(struct_line == "")
591 assert struct_name.startswith("LDK")
592 (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)
594 out_java_enum.write(native_file_out)
595 out_java.write(native_out)
597 def map_complex_enum(struct_name, union_enum_items, inline_enum_variants, enum_doc_comment):
598 java_hu_type = struct_name.replace("LDK", "").replace("COption", "Option")
601 tag_field_lines = union_enum_items["field_lines"]
602 contains_trait = False
603 for idx, (struct_line, variant_docs) in enumerate(tag_field_lines):
605 assert(struct_line == "typedef enum %s_Tag {" % struct_name)
606 elif idx == len(tag_field_lines) - 3:
607 assert(struct_line.endswith("_Sentinel,"))
608 elif idx == len(tag_field_lines) - 2:
609 assert(struct_line == "} %s_Tag;" % struct_name)
610 elif idx == len(tag_field_lines) - 1:
611 assert(struct_line == "")
613 variant_name = struct_line.strip(' ,')[len(struct_name) + 1:]
615 if "LDK" + variant_name in union_enum_items:
616 enum_var_lines = union_enum_items["LDK" + variant_name]
617 for idx, (field, field_docs) in enumerate(enum_var_lines):
618 if idx != 0 and idx < len(enum_var_lines) - 2 and field.strip() != "":
619 field_ty = type_mapping_generator.java_c_types(field.strip(' ;'), None)
620 contains_trait |= field_ty.contains_trait
621 if field_docs is not None and doc_to_field_nullable(field_docs):
622 field_conv = type_mapping_generator.map_type_with_info(field_ty, False, None, False, True, True)
624 field_conv = type_mapping_generator.map_type_with_info(field_ty, False, None, False, True, False)
625 fields.append((field_conv, field_docs))
626 enum_variants.append(ComplexEnumVariantInfo(variant_name, variant_docs, fields, False))
627 elif camel_to_snake(variant_name) in inline_enum_variants:
628 # TODO: If we ever have a rust enum Variant(Option<Struct>) we need to pipe
629 # docs through to there, and then potentially mark the field nullable.
630 mapped = type_mapping_generator.map_type(inline_enum_variants[camel_to_snake(variant_name)] + " " + camel_to_snake(variant_name), False, None, False, True)
631 contains_trait |= mapped.ty_info.contains_trait
632 fields.append((mapped, None))
633 enum_variants.append(ComplexEnumVariantInfo(variant_name, variant_docs, fields, True))
635 enum_variants.append(ComplexEnumVariantInfo(variant_name, variant_docs, fields, True))
636 complex_enums[struct_name] = contains_trait
638 with open(f"{sys.argv[3]}/structs/{java_hu_type}{consts.file_ext}", "w") as out_java_enum:
639 (out_java_addendum, out_java_enum_addendum, out_c_addendum) = consts.map_complex_enum(struct_name, enum_variants, camel_to_snake, enum_doc_comment)
641 out_java_enum.write(out_java_enum_addendum)
642 out_java.write(out_java_addendum)
643 write_c(out_c_addendum)
645 def map_trait(struct_name, field_var_lines, trait_fn_lines, trait_doc_comment):
646 with open(f"{sys.argv[3]}/structs/{struct_name.replace('LDK', '')}{consts.file_ext}", "w") as out_java_trait:
648 flattened_field_var_convs = []
649 for var_line in field_var_lines:
650 if var_line.group(1) in trait_structs:
651 field_var_convs.append((var_line.group(1), var_line.group(2), trait_structs[var_line.group(1)]))
652 flattened_field_var_convs.append((var_line.group(1), var_line.group(2), ))
653 flattened_field_var_convs.extend(trait_structs[var_line.group(1)])
655 mapped = type_mapping_generator.map_type(var_line.group(1) + " " + var_line.group(2), False, None, False, False)
656 field_var_convs.append(mapped)
657 flattened_field_var_convs.append(mapped)
658 trait_structs[struct_name] = field_var_convs
661 for fn_docs, fn_line in trait_fn_lines:
662 if fn_line == "cloned":
663 ret_ty_info = type_mapping_generator.map_type("void", True, None, False, False)
664 field_fns.append(TraitMethInfo("cloned", False, ret_ty_info, [], fn_docs))
666 (nullable_params, ret_nullable) = doc_to_params_ret_nullable(fn_docs)
668 assert False # This isn't yet handled on the Java side
669 ret_ty_info.nullable = True
670 ret_ty_info = type_mapping_generator.map_nullable_type(fn_line.group(2).strip() + " ret", True, None, False, False)
672 ret_ty_info = type_mapping_generator.map_type(fn_line.group(2).strip() + " ret", True, None, False, False)
673 is_const = fn_line.group(4) is not None
676 for idx, arg in enumerate(fn_line.group(5).split(',')):
679 arg_ty_info = type_mapping_generator.java_c_types(arg, None)
680 if arg_ty_info.var_name in nullable_params:
681 # Types that are actually null instead of all-0s aren't yet handled on the Java side:
682 arg_conv_info = type_mapping_generator.map_type_with_info(arg_ty_info, True, None, False, False, True)
684 arg_conv_info = type_mapping_generator.map_type_with_info(arg_ty_info, True, None, False, False, False)
685 arg_tys.append(arg_conv_info)
686 field_fns.append(TraitMethInfo(fn_line.group(3), is_const, ret_ty_info, arg_tys, fn_docs))
688 (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)
689 write_c(out_c_addendum)
690 out_java_trait.write(out_java_trait_addendum)
691 out_java.write(out_java_addendum)
693 for fn_docs, fn_line in trait_fn_lines:
694 if fn_line == "cloned":
696 # For now, just disable enabling the _call_log - we don't know how to inverse-map String
697 is_log = fn_line.group(3) == "log" and struct_name == "LDKLogger"
698 if fn_line.group(3) != "free" and fn_line.group(3) != "eq" and not is_log:
699 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"
700 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)
701 for idx, var_line in enumerate(field_var_lines):
702 if var_line.group(1) not in trait_structs:
703 write_c(var_line.group(1) + " " + struct_name + "_set_get_" + var_line.group(2) + "(" + struct_name + "* this_arg) {\n")
704 write_c("\tif (this_arg->set_" + var_line.group(2) + " != NULL)\n")
705 write_c("\t\tthis_arg->set_" + var_line.group(2) + "(this_arg);\n")
706 write_c("\treturn this_arg->" + var_line.group(2) + ";\n")
708 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"
709 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)
711 def map_result(struct_name, res_ty, err_ty):
712 result_types.add(struct_name)
713 human_ty = struct_name.replace("LDKCResult", "Result")
714 res_map = type_mapping_generator.map_type(res_ty + " res", True, None, False, True)
715 err_map = type_mapping_generator.map_type(err_ty + " err", True, None, False, True)
716 java_hu_struct = consts.map_result(struct_name, res_map, err_map)
717 create_getter(struct_name, res_ty, "ok", ("*", "->contents.result"), ("", "->result_ok"))
718 create_getter(struct_name, err_ty, "err", ("*", "->contents.err"), ("!", "->result_ok"))
719 with open(f"{sys.argv[3]}/structs/{human_ty}{consts.file_ext}", "w") as out_java_struct:
720 out_java_struct.write(java_hu_struct)
722 def create_getter(struct_name, field_decl, field_name, accessor, check_sfx):
723 field_ty = java_c_types(field_decl + " " + field_name, None)
724 ptr_fn_defn = field_decl + " *" + struct_name.replace("LDK", "") + "_get_" + field_name + "(" + struct_name + " *NONNULL_PTR owner)"
725 owned_fn_defn = field_decl + " " + struct_name.replace("LDK", "") + "_get_" + field_name + "(" + struct_name + " *NONNULL_PTR owner)"
728 if field_ty.rust_obj is not None and field_ty.rust_obj.replace("LDK", "") + "_clone" in clone_fns:
729 fn_defn = owned_fn_defn
730 write_c("static inline " + fn_defn + "{\n")
731 if check_sfx is not None:
732 write_c("CHECK(" + check_sfx[0] + "owner" + check_sfx[1] + ");\n")
733 write_c("\treturn " + field_ty.rust_obj.replace("LDK", "") + "_clone(&" + accessor[0] + "owner" + accessor[1] + ");\n")
734 elif field_ty.arr_len is not None or field_ty.is_native_primitive or field_ty.rust_obj in unitary_enums:
735 fn_defn = owned_fn_defn
736 write_c("static inline " + fn_defn + "{\n")
737 if check_sfx is not None:
738 write_c("CHECK(" + check_sfx[0] + "owner" + check_sfx[1] + ");\n")
739 write_c("\treturn " + accessor[0] + "owner" + accessor[1] + ";\n")
742 fn_defn = ptr_fn_defn
743 write_c("static inline " + fn_defn + "{\n")
744 if check_sfx is not None:
745 write_c("CHECK(" + check_sfx[0] + "owner" + check_sfx[1] + ");\n")
746 write_c("\treturn &" + accessor[0] + "owner" + accessor[1] + ";\n")
749 dummy_line = fn_defn + ";\n"
750 map_fn_with_ref_option(dummy_line, reg_fn_regex.match(dummy_line), None, None, "", holds_ref)
752 def map_tuple(struct_name, field_lines):
753 human_ty = struct_name.replace("LDKC2Tuple", "TwoTuple").replace("LDKC3Tuple", "ThreeTuple")
754 with open(f"{sys.argv[3]}/structs/{human_ty}{consts.file_ext}", "w") as out_java_struct:
755 out_java_struct.write(consts.map_tuple(struct_name))
757 for idx, (line, _) in enumerate(field_lines):
758 if idx != 0 and idx < len(field_lines) - 2:
759 ty_list.append(java_c_types(line.strip(';'), None))
760 tuple_types[struct_name] = (ty_list, struct_name)
762 # Map virtual getter functions
763 for idx, (line, _) in enumerate(field_lines):
764 if idx != 0 and idx < len(field_lines) - 2:
765 field_name = chr(ord('a') + idx - 1)
766 assert line.endswith(" " + field_name + ";")
767 create_getter(struct_name, line[:-3].strip(), field_name, ("", "->" + field_name), None)
769 out_java.write(consts.bindings_header)
770 with open(f"{sys.argv[2]}/version{consts.file_ext}", "w") as out_java_version:
771 out_java_version.write(consts.bindings_version_file.replace('<git_version_ldk_garbagecollected>', local_git_version))
773 with open(f"{sys.argv[3]}/structs/CommonBase{consts.file_ext}", "w") as out_java_struct:
774 out_java_struct.write(consts.common_base)
777 last_block_comment = None
780 const_val_regex = re.compile("^extern const ([A-Za-z_0-9]*) ([A-Za-z_0-9]*);$")
782 line_indicates_result_regex = re.compile("^ union (LDKCResult_[A-Za-z_0-9]*Ptr) contents;$")
783 line_indicates_vec_regex = re.compile("^ (struct |enum |union )?([A-Za-z_0-9]*) \*data;$")
784 line_indicates_opaque_regex = re.compile("^ bool is_owned;$")
785 line_indicates_trait_regex = re.compile("^ (struct |enum |union )?([A-Za-z_0-9]* \*?)\(\*([A-Za-z_0-9]*)\)\((const )?void \*this_arg(.*)\);$")
786 assert(line_indicates_trait_regex.match(" uintptr_t (*send_data)(void *this_arg, LDKu8slice data, bool resume_read);"))
787 assert(line_indicates_trait_regex.match(" struct LDKCVec_MessageSendEventZ (*get_and_clear_pending_msg_events)(const void *this_arg);"))
788 assert(line_indicates_trait_regex.match(" struct LDKCVec_u8Z (*write)(const void *this_arg);"))
789 line_indicates_trait_clone_regex = re.compile("^ void \(\*cloned\)\(struct ([A-Za-z0-9])* \*NONNULL_PTR new_[A-Za-z0-9]*\);$")
790 assert(line_indicates_trait_clone_regex.match(" void (*cloned)(struct LDKSign *NONNULL_PTR new_Sign);"))
791 line_field_var_regex = re.compile("^ struct ([A-Za-z_0-9]*) ([A-Za-z_0-9]*);$")
792 assert(line_field_var_regex.match(" struct LDKMessageSendEventsProvider MessageSendEventsProvider;"))
793 assert(line_field_var_regex.match(" struct LDKChannelPublicKeys pubkeys;"))
794 struct_name_regex = re.compile("^typedef (struct|enum|union) (MUST_USE_STRUCT )?(LDK[A-Za-z_0-9]*) {$")
795 assert(struct_name_regex.match("typedef struct LDKCVec_u8Z {"))
796 assert(struct_name_regex.match("typedef enum LDKNetwork {"))
798 union_enum_items = {}
799 result_ptr_struct_items = {}
801 if block_comment is not None:
802 if line.endswith("*/\n"):
803 last_block_comment = block_comment.strip("\n")
806 block_comment = block_comment + line.strip(" /*")
807 elif cur_block_obj is not None:
808 cur_block_obj = cur_block_obj + line
809 if line.startswith("} "):
813 obj_lines = cur_block_obj.split("\n")
815 result_contents = None
816 is_unitary_enum = False
817 is_union_enum = False
822 last_struct_block_comment = None
824 for idx, struct_line in enumerate(obj_lines):
825 if struct_line.strip().startswith("/*"):
826 block_comment = struct_line.strip(" /*")
827 if block_comment is not None:
828 if struct_line.endswith("*/"):
829 last_struct_block_comment = block_comment.strip("\n")
832 block_comment = block_comment + "\n" + struct_line.strip(" /*").replace("…", "...")
834 struct_name_match = struct_name_regex.match(struct_line)
835 if struct_name_match is not None:
836 struct_name = struct_name_match.group(3)
837 if struct_name_match.group(1) == "enum":
838 if not struct_name.endswith("_Tag"):
839 is_unitary_enum = True
842 elif struct_name_match.group(1) == "union":
844 if line_indicates_opaque_regex.match(struct_line):
846 result_match = line_indicates_result_regex.match(struct_line)
847 if result_match is not None:
848 result_contents = result_match.group(1)
849 vec_ty_match = line_indicates_vec_regex.match(struct_line)
850 if vec_ty_match is not None and struct_name.startswith("LDKCVec_"):
851 vec_ty = vec_ty_match.group(2)
852 elif struct_name.startswith("LDKC2Tuple_") or struct_name.startswith("LDKC3Tuple_"):
854 trait_fn_match = line_indicates_trait_regex.match(struct_line)
855 if trait_fn_match is not None:
856 trait_fn_lines.append((last_struct_block_comment, trait_fn_match))
857 trait_clone_fn_match = line_indicates_trait_clone_regex.match(struct_line)
858 if trait_clone_fn_match is not None:
859 trait_fn_lines.append((last_struct_block_comment, "cloned"))
860 field_var_match = line_field_var_regex.match(struct_line)
861 if field_var_match is not None:
862 field_var_lines.append(field_var_match)
863 field_lines.append((struct_line, last_struct_block_comment))
864 last_struct_block_comment = None
866 assert(struct_name is not None)
867 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))
868 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))
869 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))
870 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))
871 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))
872 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))
873 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))
876 opaque_structs.add(struct_name)
877 with open(f"{sys.argv[3]}/structs/{struct_name.replace('LDK', '')}{consts.file_ext}", "w") as out_java_struct:
878 out_opaque_struct_human = consts.map_opaque_struct(struct_name, last_block_comment)
879 last_block_comment = None
880 out_java_struct.write(out_opaque_struct_human)
881 elif result_contents is not None:
882 assert result_contents in result_ptr_struct_items
883 res_ty, err_ty = result_ptr_struct_items[result_contents]
884 map_result(struct_name, res_ty, err_ty)
885 elif struct_name.startswith("LDKCResult_") and struct_name.endswith("ZPtr"):
886 for line, _ in field_lines:
887 if line.endswith("*result;"):
888 res_ty = line[:-8].strip()
889 elif line.endswith("*err;"):
890 err_ty = line[:-5].strip()
891 result_ptr_struct_items[struct_name] = (res_ty, err_ty)
892 result_types.add(struct_name[:-3])
894 map_tuple(struct_name, field_lines)
895 elif vec_ty is not None:
896 ty_info = type_mapping_generator.map_type(vec_ty + " arr_elem", False, None, False, False)
897 if ty_info.is_native_primitive:
898 clone_fns.add(struct_name.replace("LDK", "") + "_clone")
899 write_c("static inline " + struct_name + " " + struct_name.replace("LDK", "") + "_clone(const " + struct_name + " *orig) {\n")
900 write_c("\t" + struct_name + " ret = { .data = MALLOC(sizeof(" + ty_info.c_ty + ") * orig->datalen, \"" + struct_name + " clone bytes\"), .datalen = orig->datalen };\n")
901 write_c("\tmemcpy(ret.data, orig->data, sizeof(" + ty_info.c_ty + ") * ret.datalen);\n")
902 write_c("\treturn ret;\n}\n")
903 elif (ty_info.rust_obj.replace("LDK", "") + "_clone") in clone_fns:
904 ty_name = "CVec_" + ty_info.rust_obj.replace("LDK", "") + "Z";
905 clone_fns.add(ty_name + "_clone")
906 write_c("static inline " + struct_name + " " + ty_name + "_clone(const " + struct_name + " *orig) {\n")
907 write_c("\t" + struct_name + " ret = { .data = MALLOC(sizeof(" + ty_info.rust_obj + ") * orig->datalen, \"" + struct_name + " clone bytes\"), .datalen = orig->datalen };\n")
908 write_c("\tfor (size_t i = 0; i < ret.datalen; i++) {\n")
909 write_c("\t\tret.data[i] = " + ty_info.rust_obj.replace("LDK", "") + "_clone(&orig->data[i]);\n")
910 write_c("\t}\n\treturn ret;\n}\n")
912 assert(struct_name.endswith("_Tag"))
913 struct_name = struct_name[:-4]
914 union_enum_items[struct_name] = {"field_lines": field_lines}
915 elif struct_name.endswith("_Body") and struct_name.split("_")[0] in union_enum_items:
916 enum_var_name = struct_name.split("_")
917 union_enum_items[enum_var_name[0]][enum_var_name[1]] = field_lines
918 elif struct_name in union_enum_items:
921 for line, _ in field_lines:
922 if line == " struct {":
926 elif elem_items > -1:
928 if line.startswith("struct "):
930 elif line.startswith("enum "):
932 split = line.split(" ")
933 assert len(split) == 2
934 tuple_variants[split[1].strip(";")] = split[0]
937 # We don't currently support tuple variant with more than one element
939 map_complex_enum(struct_name, union_enum_items[struct_name], tuple_variants, last_block_comment)
940 last_block_comment = None
941 elif is_unitary_enum:
942 map_unitary_enum(struct_name, field_lines, last_block_comment)
943 last_block_comment = None
944 elif len(trait_fn_lines) > 0:
945 map_trait(struct_name, field_var_lines, trait_fn_lines, last_block_comment)
946 elif struct_name == "LDKTxOut":
947 with open(f"{sys.argv[3]}/structs/TxOut{consts.file_ext}", "w") as out_java_struct:
948 out_java_struct.write(consts.hu_struct_file_prefix)
949 out_java_struct.write(consts.txout_defn)
950 fn_line = "struct LDKCVec_u8Z TxOut_get_script_pubkey (struct LDKTxOut* thing)"
951 write_c(fn_line + " {")
952 write_c("\treturn CVec_u8Z_clone(&thing->script_pubkey);")
954 map_fn(fn_line + "\n", re.compile("(.*) (TxOut_get_script_pubkey) \((.*)\)").match(fn_line), None, None, None)
955 fn_line = "uint64_t TxOut_get_value (struct LDKTxOut* thing)"
956 write_c(fn_line + " {")
957 write_c("\treturn thing->value;")
959 map_fn(fn_line + "\n", re.compile("(.*) (TxOut_get_value) \((.*)\)").match(fn_line), None, None, None)
961 pass # Everything remaining is a byte[] or some form
964 fn_ptr = fn_ptr_regex.match(line)
965 fn_ret_arr = fn_ret_arr_regex.match(line)
966 reg_fn = reg_fn_regex.match(line)
967 const_val = const_val_regex.match(line)
969 if line.startswith("#include <"):
971 elif line.startswith("/*"):
972 if not line.endswith("*/\n"):
973 block_comment = line.strip(" /*")
974 elif line.startswith("typedef enum "):
976 elif line.startswith("typedef struct "):
978 elif line.startswith("typedef union "):
980 elif fn_ptr is not None:
981 map_fn(line, fn_ptr, None, None, last_block_comment)
982 last_block_comment = None
983 elif fn_ret_arr is not None:
984 map_fn(line, fn_ret_arr, fn_ret_arr.group(4), None, last_block_comment)
985 last_block_comment = None
986 elif reg_fn is not None:
987 map_fn(line, reg_fn, None, None, last_block_comment)
988 last_block_comment = None
989 elif const_val_regex is not None:
990 # TODO Map const variables
995 out_java.write(consts.bindings_footer)
996 for struct_name in opaque_structs:
997 with open(f"{sys.argv[3]}/structs/{struct_name.replace('LDK', '')}{consts.file_ext}", "a") as out_java_struct:
998 out_java_struct.write("}\n")
999 for struct_name in trait_structs:
1000 with open(f"{sys.argv[3]}/structs/{struct_name.replace('LDK', '')}{consts.file_ext}", "a") as out_java_struct:
1001 out_java_struct.write("}\n")
1002 for struct_name in complex_enums:
1003 with open(f"{sys.argv[3]}/structs/{struct_name.replace('LDK', '').replace('COption', 'Option')}{consts.file_ext}", "a") as out_java_struct:
1004 out_java_struct.write("}\n")
1005 for struct_name in result_types:
1006 with open(f"{sys.argv[3]}/structs/{struct_name.replace('LDKCResult', 'Result')}{consts.file_ext}", "a") as out_java_struct:
1007 out_java_struct.write("}\n")
1008 for struct_name in tuple_types:
1009 struct_hu_name = struct_name.replace("LDKC2Tuple", "TwoTuple").replace("LDKC3Tuple", "ThreeTuple")
1010 with open(f"{sys.argv[3]}/structs/{struct_hu_name}{consts.file_ext}", "a") as out_java_struct:
1011 out_java_struct.write("}\n")
1013 with open(f"{sys.argv[4]}/bindings.c.body", "w") as out_c:
1014 out_c.write(consts.c_file_pfx)
1015 out_c.write(consts.init_str())
1017 with open(f"{sys.argv[4]}/version.c", "w") as out_c:
1018 out_c.write(consts.c_version_file.replace('<git_version_ldk_garbagecollected>', local_git_version))
1019 with open(f"{sys.argv[3]}/structs/UtilMethods{consts.file_ext}", "a") as util:
1020 util.write(consts.util_fn_sfx)