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("LDKPaymentPreimage") or fn_arg.startswith("LDKPaymentSecret") or fn_arg.startswith("LDKPaymentHash"):
128 if fn_arg.startswith("LDKPaymentPreimage"):
129 fn_arg = "uint8_t (*" + fn_arg[19:] + ")[32]"
130 elif fn_arg.startswith("LDKPaymentSecret"):
131 fn_arg = "uint8_t (*" + fn_arg[17:] + ")[32]"
132 elif fn_arg.startswith("LDKPaymentHash"):
133 fn_arg = "uint8_t (*" + fn_arg[15:] + ")[32]"
134 assert var_is_arr_regex.match(fn_arg[8:])
135 rust_obj = "LDKThirtyTwoBytes"
137 elif fn_arg.startswith("LDKThirtyTwoBytes"):
138 fn_arg = "uint8_t (*" + fn_arg[18:] + ")[32]"
139 assert var_is_arr_regex.match(fn_arg[8:])
140 rust_obj = "LDKThirtyTwoBytes"
142 elif fn_arg.startswith("LDKTxid"):
143 fn_arg = "uint8_t (*" + fn_arg[8:] + ")[32]"
144 assert var_is_arr_regex.match(fn_arg[8:])
145 rust_obj = "LDKThirtyTwoBytes"
147 elif fn_arg.startswith("LDKPublicKey"):
148 fn_arg = "uint8_t (*" + fn_arg[13:] + ")[33]"
149 assert var_is_arr_regex.match(fn_arg[8:])
150 rust_obj = "LDKPublicKey"
151 arr_access = "compressed_form"
152 elif fn_arg.startswith("LDKSecretKey"):
153 fn_arg = "uint8_t (*" + fn_arg[13:] + ")[32]"
154 assert var_is_arr_regex.match(fn_arg[8:])
155 rust_obj = "LDKSecretKey"
157 elif fn_arg.startswith("LDKSignature"):
158 fn_arg = "uint8_t (*" + fn_arg[13:] + ")[64]"
159 assert var_is_arr_regex.match(fn_arg[8:])
160 rust_obj = "LDKSignature"
161 arr_access = "compact_form"
162 elif fn_arg.startswith("LDKRecoverableSignature"):
163 fn_arg = "uint8_t (*" + fn_arg[24:] + ")[68]"
164 assert var_is_arr_regex.match(fn_arg[8:])
165 rust_obj = "LDKRecoverableSignature"
166 arr_access = "serialized_form"
167 elif fn_arg.startswith("LDKThreeBytes"):
168 fn_arg = "uint8_t (*" + fn_arg[14:] + ")[3]"
169 assert var_is_arr_regex.match(fn_arg[8:])
170 rust_obj = "LDKThreeBytes"
172 elif fn_arg.startswith("LDKFourBytes"):
173 fn_arg = "uint8_t (*" + fn_arg[13:] + ")[4]"
174 assert var_is_arr_regex.match(fn_arg[8:])
175 rust_obj = "LDKFourBytes"
177 elif fn_arg.startswith("LDKSixteenBytes"):
178 fn_arg = "uint8_t (*" + fn_arg[16:] + ")[16]"
179 assert var_is_arr_regex.match(fn_arg[8:])
180 rust_obj = "LDKSixteenBytes"
182 elif fn_arg.startswith("LDKTwentyBytes"):
183 fn_arg = "uint8_t (*" + fn_arg[15:] + ")[20]"
184 assert var_is_arr_regex.match(fn_arg[8:])
185 rust_obj = "LDKTwentyBytes"
187 elif fn_arg.startswith("LDKTwelveBytes"):
188 fn_arg = "uint8_t (*" + fn_arg[15:] + ")[12]"
189 assert var_is_arr_regex.match(fn_arg[8:])
190 rust_obj = "LDKTwelveBytes"
192 elif fn_arg.startswith("LDKu8slice"):
193 fn_arg = "uint8_t (*" + fn_arg[11:] + ")[datalen]"
194 assert var_is_arr_regex.match(fn_arg[8:])
195 rust_obj = "LDKu8slice"
197 elif fn_arg.startswith("LDKCVec_u8Z"):
198 fn_arg = "uint8_t (*" + fn_arg[12:] + ")[datalen]"
199 rust_obj = "LDKCVec_u8Z"
200 assert var_is_arr_regex.match(fn_arg[8:])
202 elif fn_arg.startswith("LDKTransaction ") or fn_arg == "LDKTransaction":
203 fn_arg = "uint8_t (*" + fn_arg[15:] + ")[datalen]"
204 rust_obj = "LDKTransaction"
205 assert var_is_arr_regex.match(fn_arg[8:])
207 elif fn_arg.startswith("LDKCVec_"):
210 fn_arg = fn_arg.replace("*", "")
213 tyn = fn_arg[8:].split(" ")
214 assert tyn[0].endswith("Z")
218 new_arg = "LDK" + tyn[0][:-1]
220 new_arg = new_arg + " " + a
221 res = java_c_types(new_arg, ret_arr_len)
223 assert java_c_types_none_allowed
226 res.pass_by_ref = True
227 java_ty = consts.java_arr_ty_str(res.java_ty)
228 if res.is_native_primitive or res.passed_as_ptr:
229 return TypeInfo(rust_obj=fn_arg.split(" ")[0], java_ty=java_ty, java_hu_ty=res.java_hu_ty + "[]",
230 java_fn_ty_arg="[" + res.java_fn_ty_arg, c_ty=res.c_ty + "Array", passed_as_ptr=False, is_ptr=is_ptr,
231 nonnull_ptr=nonnull_ptr, is_const=is_const,
232 var_name=res.var_name, arr_len="datalen", arr_access="data", subty=res, is_native_primitive=False)
234 return TypeInfo(rust_obj=fn_arg.split(" ")[0], java_ty=java_ty, java_hu_ty=res.java_hu_ty + "[]",
235 java_fn_ty_arg="[" + res.java_fn_ty_arg, c_ty=consts.ptr_arr, passed_as_ptr=False, is_ptr=is_ptr,
236 nonnull_ptr=nonnull_ptr, is_const=is_const,
237 var_name=res.var_name, arr_len="datalen", arr_access="data", subty=res, is_native_primitive=False)
240 contains_trait = False
243 java_type_plural = None
245 if fn_arg.startswith("void"):
249 fn_arg = fn_arg[4:].strip()
251 elif fn_arg.startswith("bool"):
256 fn_arg = fn_arg[4:].strip()
258 elif fn_arg.startswith("uint8_t"):
259 mapped_type = consts.c_type_map['uint8_t']
260 java_ty = mapped_type[0]
264 fn_arg = fn_arg[7:].strip()
266 elif fn_arg.startswith("LDKu5"):
267 java_ty = consts.c_type_map['uint8_t'][0]
273 fn_arg = fn_arg[6:].strip()
274 elif fn_arg.startswith("uint16_t"):
275 mapped_type = consts.c_type_map['uint16_t']
276 java_ty = mapped_type[0]
280 fn_arg = fn_arg[8:].strip()
282 elif fn_arg.startswith("uint32_t"):
283 mapped_type = consts.c_type_map['uint32_t']
284 java_ty = mapped_type[0]
288 fn_arg = fn_arg[8:].strip()
290 elif fn_arg.startswith("uint64_t") or fn_arg.startswith("uintptr_t"):
291 # TODO: uintptr_t is arch-dependent :(
292 mapped_type = consts.c_type_map['uint64_t']
293 java_ty = mapped_type[0]
295 if fn_arg.startswith("uint64_t"):
298 fn_arg = fn_arg[8:].strip()
300 java_ty = consts.ptr_native_ty
301 c_ty = consts.ptr_c_ty
303 rust_obj = "uintptr_t"
304 fn_arg = fn_arg[9:].strip()
306 elif is_const and fn_arg.startswith("char *"):
307 java_ty = consts.java_type_map["String"]
308 java_hu_ty = consts.java_hu_type_map["String"]
311 fn_ty_arg = "Ljava/lang/String;"
312 fn_arg = fn_arg[6:].strip()
313 elif fn_arg.startswith("LDKStr"):
316 java_ty = consts.java_type_map["String"]
317 java_hu_ty = consts.java_hu_type_map["String"]
319 fn_ty_arg = "Ljava/lang/String;"
320 fn_arg = fn_arg[6:].strip()
323 elif fn_arg.startswith("LDKError ") or fn_arg == "LDKError":
324 java_ty = consts.c_type_map['uint32_t'][0]
325 java_hu_ty = "UnqualifiedError"
326 rust_obj = "LDKError"
330 fn_arg = fn_arg[8:].strip()
332 ma = var_ty_regex.match(fn_arg)
333 arr_ty = ma.group(1).strip()
334 if ma.group(1).strip() in unitary_enums:
335 assert ma.group(1).strip().startswith("LDK")
336 java_ty = ma.group(1).strip()[3:]
338 c_ty = consts.result_c_ty
339 fn_ty_arg = "Lorg/ldk/enums/" + java_ty + ";"
340 fn_arg = ma.group(2).strip()
341 rust_obj = ma.group(1).strip()
343 c_ty = consts.ptr_c_ty
344 java_ty = consts.ptr_native_ty
345 java_hu_ty = ma.group(1).strip()
346 java_hu_ty = java_hu_ty.replace("LDKCOption", "Option")
347 java_hu_ty = java_hu_ty.replace("LDKCResult", "Result")
348 java_hu_ty = java_hu_ty.replace("LDKC2Tuple", "TwoTuple")
349 java_hu_ty = java_hu_ty.replace("LDKC3Tuple", "ThreeTuple")
350 java_hu_ty = java_hu_ty.replace("LDK", "")
352 fn_arg = ma.group(2).strip()
353 rust_obj = ma.group(1).strip()
354 if rust_obj in trait_structs:
355 contains_trait = True
356 elif rust_obj in complex_enums:
357 contains_trait = complex_enums[rust_obj]
360 if fn_arg.startswith(" *") or fn_arg.startswith("*"):
361 fn_arg = fn_arg.replace("*", "").strip()
363 c_ty = consts.ptr_c_ty
364 java_ty = consts.ptr_native_ty
367 var_is_arr = var_is_arr_regex.match(fn_arg)
369 if var_is_arr is not None or ret_arr_len is not None:
370 assert(not take_by_ptr)
372 # is there a special case for plurals?
373 if len(mapped_type) == 3:
374 java_ty = mapped_type[1]
375 java_hu_ty = mapped_type[2]
377 java_ty = java_ty + "[]"
379 c_ty = c_ty + "Array"
381 subty = java_c_types(arr_ty, None)
383 assert java_c_types_none_allowed
386 subty.pass_by_ref = True
388 if var_is_arr is not None:
389 if var_is_arr.group(1) == "":
390 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, is_const=is_const,
391 passed_as_ptr=False, is_ptr=False, nonnull_ptr=nonnull_ptr, var_name="arg", subty=subty,
392 arr_len=var_is_arr.group(2), arr_access=arr_access, is_native_primitive=False, contains_trait=contains_trait)
393 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, is_const=is_const,
394 passed_as_ptr=False, is_ptr=False, nonnull_ptr=nonnull_ptr, var_name=var_is_arr.group(1), subty=subty,
395 arr_len=var_is_arr.group(2), arr_access=arr_access, is_native_primitive=False, contains_trait=contains_trait)
397 if java_hu_ty is None:
399 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,
400 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,
401 contains_trait=contains_trait, subty=subty)
403 fn_ptr_regex = re.compile("^extern const ([A-Za-z_0-9\* ]*) \(\*(.*)\)\((.*)\);$")
404 fn_ret_arr_regex = re.compile("(.*) \(\*(.*)\((.*)\)\)\[([0-9]*)\];$")
405 reg_fn_regex = re.compile("([A-Za-z_0-9\* ]* \*?)([a-zA-Z_0-9]*)\((.*)\);$")
409 from gen_type_mapping import TypeMappingGenerator
410 type_mapping_generator = TypeMappingGenerator(java_c_types, consts, opaque_structs, clone_fns, unitary_enums, trait_structs, complex_enums, result_types, tuple_types)
412 with open(sys.argv[1]) as in_h:
414 reg_fn = reg_fn_regex.match(line)
415 if reg_fn is not None:
416 if reg_fn.group(2).endswith("_clone"):
417 clone_fns.add(reg_fn.group(2))
419 rty = java_c_types(reg_fn.group(1), None)
420 if rty is not None and not rty.is_native_primitive and reg_fn.group(2) == rty.java_hu_ty + "_new":
421 constructor_fns[rty.rust_obj] = reg_fn.group(3)
423 arr_fn = fn_ret_arr_regex.match(line)
424 if arr_fn is not None:
425 if arr_fn.group(2).endswith("_clone"):
426 clone_fns.add(arr_fn.group(2))
427 # No object constructors return arrays, as then they wouldn't be an object constructor
430 # Define some manual clones...
431 clone_fns.add("ThirtyTwoBytes_clone")
432 write_c("static inline struct LDKThirtyTwoBytes ThirtyTwoBytes_clone(const struct LDKThirtyTwoBytes *orig) { struct LDKThirtyTwoBytes ret; memcpy(ret.data, orig->data, 32); return ret; }\n")
434 java_c_types_none_allowed = False # C structs created by cbindgen are declared in dependency order
436 with open(f"{sys.argv[3]}/structs/UtilMethods{consts.file_ext}", "a") as util:
437 util.write(consts.util_fn_pfx)
439 with open(sys.argv[1]) as in_h, open(f"{sys.argv[2]}/bindings{consts.file_ext}", "w") as out_java:
440 # Map a top-level function
441 def map_fn(line, re_match, ret_arr_len, c_call_string, doc_comment):
442 map_fn_with_ref_option(line, re_match, ret_arr_len, c_call_string, doc_comment, False)
443 def map_fn_with_ref_option(line, re_match, ret_arr_len, c_call_string, doc_comment, force_holds_ref):
444 method_return_type = re_match.group(1)
445 method_name = re_match.group(2)
446 method_comma_separated_arguments = re_match.group(3)
447 method_arguments = method_comma_separated_arguments.split(',')
449 if method_name.startswith("__"):
452 is_free = method_name.endswith("_free")
453 if method_name.startswith("COption") or method_name.startswith("CResult"):
454 struct_meth = method_name.rsplit("Z", 1)[0][1:] + "Z"
455 expected_struct = "LDKC" + struct_meth
456 struct_meth_name = method_name[len(struct_meth) + 1:].strip("_")
457 elif method_name.startswith("C2Tuple") or method_name.startswith("C3Tuple"):
458 tuple_name = method_name.rsplit("Z", 1)[0][2:] + "Z"
459 if method_name.startswith("C2Tuple"):
460 struct_meth = "Two" + tuple_name
461 expected_struct = "LDKC2" + tuple_name
463 struct_meth = "Three" + tuple_name
464 expected_struct = "LDKC3" + tuple_name
465 struct_meth_name = method_name[len(tuple_name) + 2:].strip("_")
467 struct_meth = method_name.split("_")[0]
468 expected_struct = "LDK" + struct_meth
469 struct_meth_name = method_name[len(struct_meth) + 1 if len(struct_meth) != 0 else 0:].strip("_")
471 (params_nullable, ret_nullable) = doc_to_params_ret_nullable(doc_comment)
473 return_type_info = type_mapping_generator.map_nullable_type(method_return_type.strip() + " ret", True, ret_arr_len, False, force_holds_ref)
475 return_type_info = type_mapping_generator.map_type(method_return_type.strip() + " ret", True, ret_arr_len, False, force_holds_ref)
477 if method_name.endswith("_clone") and expected_struct not in unitary_enums:
478 meth_line = "uintptr_t " + expected_struct.replace("LDK", "") + "_clone_ptr(" + expected_struct + " *NONNULL_PTR arg)"
479 write_c("static inline " + meth_line + " {\n")
480 write_c("\t" + return_type_info.ret_conv[0].replace("\n", "\n\t"))
481 write_c(method_name + "(arg)")
482 write_c(return_type_info.ret_conv[1])
483 write_c("\n\treturn " + return_type_info.ret_conv_name + ";\n}\n")
484 map_fn(meth_line + ";\n", re.compile("(uintptr_t) ([A-Za-z_0-9]*)\((.*)\)").match(meth_line), None, None, None)
487 default_constructor_args = {}
489 takes_self_ptr = False
492 for argument_index, argument in enumerate(method_arguments):
493 arg_ty = type_mapping_generator.java_c_types(argument, None)
494 argument_conversion_info = None
495 if argument_index == 0 and arg_ty.java_hu_ty == struct_meth:
496 argument_conversion_info = type_mapping_generator.map_type_with_info(arg_ty, False, None, is_free, True, False)
498 if argument_conversion_info.ty_info.is_ptr:
499 takes_self_ptr = True
500 elif arg_ty.var_name in params_nullable:
501 argument_conversion_info = type_mapping_generator.map_type_with_info(arg_ty, False, None, is_free, True, True)
502 if argument_conversion_info.arg_conv is not None and "WARNING" in argument_conversion_info.arg_conv:
503 arg_ty_info = java_c_types(argument, None)
504 print("WARNING: Remapping argument " + arg_ty_info.var_name + " of function " + method_name + " to a reference")
505 print(" The argument appears to require a move, or not clonable, and is nullable.")
506 print(" Normally for arguments that require a move and are not clonable, we split")
507 print(" the argument into the type's constructor's arguments and just use those to")
508 print(" construct a new object on the fly.")
509 print(" However, because the object is nullable, doing so would mean we can no")
510 print(" longer allow the user to pass null, as we now have an argument list instead.")
511 print(" Thus, we blindly assume its really an Option<&Type> instead of an Option<Type>.")
512 print(" It may or may not actually be a reference, but its the simplest mapping option")
513 print(" and also the only use of this code today.")
514 arg_ty_info.requires_clone = False
515 argument_conversion_info = type_mapping_generator.map_type_with_info(arg_ty_info, False, None, is_free, True, True)
516 assert argument_conversion_info.nullable
517 assert argument_conversion_info.arg_conv is not None and "WARNING" not in argument_conversion_info.arg_conv
519 argument_conversion_info = type_mapping_generator.map_type_with_info(arg_ty, False, None, is_free, True, False)
521 if argument_conversion_info.arg_conv is not None and "WARNING" in argument_conversion_info.arg_conv:
522 if argument_conversion_info.rust_obj in constructor_fns:
524 for explode_arg in constructor_fns[argument_conversion_info.rust_obj].split(','):
525 explode_arg_conv = type_mapping_generator.map_type(explode_arg, False, None, False, True)
526 if explode_arg_conv.c_ty == "void":
527 # We actually want to handle this case, but for now its only used in NetGraphMsgHandler::new()
528 # which ends up resulting in a redundant constructor - both without arguments for the NetworkGraph.
531 if not argument_conversion_info.arg_name in default_constructor_args:
532 default_constructor_args[argument_conversion_info.arg_name] = []
533 default_constructor_args[argument_conversion_info.arg_name].append(explode_arg_conv)
534 argument_types.append(argument_conversion_info)
536 if not takes_self and return_type_info.java_hu_ty != struct_meth:
537 if not return_type_info.java_hu_ty.startswith("Result_" + struct_meth):
538 struct_meth_name = method_name
542 impl_on_struct = (expected_struct in opaque_structs or expected_struct in trait_structs or
543 expected_struct in complex_enums or expected_struct in complex_enums or
544 expected_struct in result_types or expected_struct in tuple_types) and not is_free
545 impl_on_utils = not impl_on_struct and (not is_free and not method_name.endswith("_clone") and
546 not method_name.startswith("TxOut") and
547 not method_name.startswith("_") and
548 method_name != "check_platform" and method_name != "Result_read" and
549 not expected_struct in unitary_enums and
550 ((not method_name.startswith("C2Tuple_") and not method_name.startswith("C3Tuple_"))
551 or method_name.endswith("_read")))
553 # If we're adding a static method, and it returns a primitive or an array of primitives,
554 # and a variable conversion adds a reference on the return type (via `this`), skip the
555 # variable's conversion reference-add (as we obviously cannot need a reference).
556 if impl_on_utils and (return_type_info.is_native_primitive or
557 (return_type_info.ty_info.subty is not None and return_type_info.ty_info.subty.is_native_primitive)):
558 for arg in argument_types:
559 if arg.from_hu_conv is not None and arg.from_hu_conv[1] != "":
560 if "this" in arg.from_hu_conv[1]:
561 arg.from_hu_conv = (arg.from_hu_conv[0], "")
563 out_java.write("\t// " + line)
564 (out_java_delta, out_c_delta, out_java_struct_delta) = \
565 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)
566 out_java.write(out_java_delta)
569 assert len(argument_types) == 1
570 assert return_type_info.c_ty == "void"
571 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")
572 if argument_types[0].ty_info.passed_as_ptr and not argument_types[0].ty_info.rust_obj in opaque_structs:
573 write_c("\tif ((" + argument_types[0].ty_info.var_name + " & 1) != 0) return;\n")
575 for info in argument_types:
576 if info.arg_conv is not None:
577 write_c("\t" + info.arg_conv.replace('\n', "\n\t") + "\n")
578 assert c_call_string is None
579 write_c("\t" + method_name + "(")
580 if argument_types[0].arg_conv_name is not None:
581 write_c(argument_types[0].arg_conv_name)
583 for info in argument_types:
584 if info.arg_conv_cleanup is not None:
585 write_c("\n\t" + info.arg_conv_cleanup.replace("\n", "\n\t"))
590 out_java_struct = None
592 out_java_struct = open(f"{sys.argv[3]}/structs/{struct_meth}{consts.file_ext}", "a")
593 out_java_struct.write(out_java_struct_delta)
595 out_java_struct = open(f"{sys.argv[3]}/structs/UtilMethods{consts.file_ext}", "a")
596 for line in out_java_struct_delta.splitlines():
597 out_java_struct.write(line + "\n")
599 def map_unitary_enum(struct_name, field_lines, enum_doc_comment):
600 assert struct_name.startswith("LDK")
601 with open(f"{sys.argv[3]}/enums/{struct_name[3:]}{consts.file_ext}", "w") as out_java_enum:
602 unitary_enums.add(struct_name)
603 for idx, (struct_line, _) in enumerate(field_lines):
605 assert(struct_line == "typedef enum %s {" % struct_name)
606 elif idx == len(field_lines) - 3:
607 assert(struct_line.endswith("_Sentinel,"))
608 elif idx == len(field_lines) - 2:
609 assert(struct_line == "} %s;" % struct_name)
610 elif idx == len(field_lines) - 1:
611 assert(struct_line == "")
612 assert struct_name.startswith("LDK")
613 (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)
615 out_java_enum.write(native_file_out)
616 out_java.write(native_out)
618 def map_complex_enum(struct_name, union_enum_items, inline_enum_variants, enum_doc_comment):
619 java_hu_type = struct_name.replace("LDK", "").replace("COption", "Option")
622 tag_field_lines = union_enum_items["field_lines"]
623 contains_trait = False
624 for idx, (struct_line, variant_docs) in enumerate(tag_field_lines):
626 assert(struct_line == "typedef enum %s_Tag {" % struct_name)
627 elif idx == len(tag_field_lines) - 3:
628 assert(struct_line.endswith("_Sentinel,"))
629 elif idx == len(tag_field_lines) - 2:
630 assert(struct_line == "} %s_Tag;" % struct_name)
631 elif idx == len(tag_field_lines) - 1:
632 assert(struct_line == "")
634 variant_name = struct_line.strip(' ,')[len(struct_name) + 1:]
636 if "LDK" + variant_name in union_enum_items:
637 enum_var_lines = union_enum_items["LDK" + variant_name]
638 for idx, (field, field_docs) in enumerate(enum_var_lines):
639 if idx != 0 and idx < len(enum_var_lines) - 2 and field.strip() != "":
640 field_ty = type_mapping_generator.java_c_types(field.strip(' ;'), None)
641 contains_trait |= field_ty.contains_trait
642 if field_docs is not None and doc_to_field_nullable(field_docs):
643 field_conv = type_mapping_generator.map_type_with_info(field_ty, False, None, False, True, True)
645 field_conv = type_mapping_generator.map_type_with_info(field_ty, False, None, False, True, False)
646 fields.append((field_conv, field_docs))
647 enum_variants.append(ComplexEnumVariantInfo(variant_name, variant_docs, fields, False))
648 elif camel_to_snake(variant_name) in inline_enum_variants:
649 # TODO: If we ever have a rust enum Variant(Option<Struct>) we need to pipe
650 # docs through to there, and then potentially mark the field nullable.
651 mapped = type_mapping_generator.map_type(inline_enum_variants[camel_to_snake(variant_name)] + " " + camel_to_snake(variant_name), False, None, False, True)
652 contains_trait |= mapped.ty_info.contains_trait
653 fields.append((mapped, None))
654 enum_variants.append(ComplexEnumVariantInfo(variant_name, variant_docs, fields, True))
656 enum_variants.append(ComplexEnumVariantInfo(variant_name, variant_docs, fields, True))
657 complex_enums[struct_name] = contains_trait
659 with open(f"{sys.argv[3]}/structs/{java_hu_type}{consts.file_ext}", "w") as out_java_enum:
660 (out_java_addendum, out_java_enum_addendum, out_c_addendum) = consts.map_complex_enum(struct_name, enum_variants, camel_to_snake, enum_doc_comment)
662 out_java_enum.write(out_java_enum_addendum)
663 out_java.write(out_java_addendum)
664 write_c(out_c_addendum)
666 def map_trait(struct_name, field_var_lines, trait_fn_lines, trait_doc_comment):
667 with open(f"{sys.argv[3]}/structs/{struct_name.replace('LDK', '')}{consts.file_ext}", "w") as out_java_trait:
669 flattened_field_var_convs = []
670 for var_line in field_var_lines:
671 if var_line.group(1) in trait_structs:
672 field_var_convs.append((var_line.group(1), var_line.group(2), trait_structs[var_line.group(1)]))
673 flattened_field_var_convs.append((var_line.group(1), var_line.group(2), ))
674 flattened_field_var_convs.extend(trait_structs[var_line.group(1)])
676 mapped = type_mapping_generator.map_type(var_line.group(1) + " " + var_line.group(2), False, None, False, False)
677 field_var_convs.append(mapped)
678 flattened_field_var_convs.append(mapped)
679 trait_structs[struct_name] = field_var_convs
682 for fn_docs, fn_line in trait_fn_lines:
683 if fn_line == "cloned":
684 ret_ty_info = type_mapping_generator.map_type("void", True, None, False, False)
685 field_fns.append(TraitMethInfo("cloned", False, ret_ty_info, [], fn_docs))
687 (nullable_params, ret_nullable) = doc_to_params_ret_nullable(fn_docs)
689 assert False # This isn't yet handled on the Java side
690 ret_ty_info.nullable = True
691 ret_ty_info = type_mapping_generator.map_nullable_type(fn_line.group(2).strip() + " ret", True, None, False, False)
693 ret_ty_info = type_mapping_generator.map_type(fn_line.group(2).strip() + " ret", True, None, False, False)
694 is_const = fn_line.group(4) is not None
697 for idx, arg in enumerate(fn_line.group(5).split(',')):
700 arg_ty_info = type_mapping_generator.java_c_types(arg, None)
701 if arg_ty_info.var_name in nullable_params:
702 # Types that are actually null instead of all-0s aren't yet handled on the Java side:
703 arg_conv_info = type_mapping_generator.map_type_with_info(arg_ty_info, True, None, False, False, True)
705 arg_conv_info = type_mapping_generator.map_type_with_info(arg_ty_info, True, None, False, False, False)
706 arg_tys.append(arg_conv_info)
707 field_fns.append(TraitMethInfo(fn_line.group(3), is_const, ret_ty_info, arg_tys, fn_docs))
709 (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)
710 write_c(out_c_addendum)
711 out_java_trait.write(out_java_trait_addendum)
712 out_java.write(out_java_addendum)
714 for fn_docs, fn_line in trait_fn_lines:
715 if fn_line == "cloned":
717 # For now, just disable enabling the _call_log - we don't know how to inverse-map String
718 is_log = fn_line.group(3) == "log" and struct_name == "LDKLogger"
719 if fn_line.group(3) != "free" and fn_line.group(3) != "eq" and not is_log:
720 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"
721 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)
722 for idx, var_line in enumerate(field_var_lines):
723 if var_line.group(1) not in trait_structs:
724 write_c(var_line.group(1) + " " + struct_name + "_set_get_" + var_line.group(2) + "(" + struct_name + "* this_arg) {\n")
725 write_c("\tif (this_arg->set_" + var_line.group(2) + " != NULL)\n")
726 write_c("\t\tthis_arg->set_" + var_line.group(2) + "(this_arg);\n")
727 write_c("\treturn this_arg->" + var_line.group(2) + ";\n")
729 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"
730 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)
732 def map_result(struct_name, res_ty, err_ty):
733 result_types.add(struct_name)
734 human_ty = struct_name.replace("LDKCResult", "Result")
735 res_map = type_mapping_generator.map_type(res_ty + " res", True, None, False, True)
736 err_map = type_mapping_generator.map_type(err_ty + " err", True, None, False, True)
737 java_hu_struct = consts.map_result(struct_name, res_map, err_map)
738 create_getter(struct_name, res_ty, "ok", ("*", "->contents.result"), ("", "->result_ok"))
739 create_getter(struct_name, err_ty, "err", ("*", "->contents.err"), ("!", "->result_ok"))
740 with open(f"{sys.argv[3]}/structs/{human_ty}{consts.file_ext}", "w") as out_java_struct:
741 out_java_struct.write(java_hu_struct)
743 def create_getter(struct_name, field_decl, field_name, accessor, check_sfx):
744 field_ty = java_c_types(field_decl + " " + field_name, None)
745 ptr_fn_defn = field_decl + " *" + struct_name.replace("LDK", "") + "_get_" + field_name + "(" + struct_name + " *NONNULL_PTR owner)"
746 owned_fn_defn = field_decl + " " + struct_name.replace("LDK", "") + "_get_" + field_name + "(" + struct_name + " *NONNULL_PTR owner)"
749 if field_ty.rust_obj is not None and field_ty.rust_obj.replace("LDK", "") + "_clone" in clone_fns:
750 fn_defn = owned_fn_defn
751 write_c("static inline " + fn_defn + "{\n")
752 if check_sfx is not None:
753 write_c("CHECK(" + check_sfx[0] + "owner" + check_sfx[1] + ");\n")
754 write_c("\treturn " + field_ty.rust_obj.replace("LDK", "") + "_clone(&" + accessor[0] + "owner" + accessor[1] + ");\n")
755 elif field_ty.arr_len is not None or field_ty.is_native_primitive or field_ty.rust_obj in unitary_enums:
756 fn_defn = owned_fn_defn
757 write_c("static inline " + fn_defn + "{\n")
758 if check_sfx is not None:
759 write_c("CHECK(" + check_sfx[0] + "owner" + check_sfx[1] + ");\n")
760 write_c("\treturn " + accessor[0] + "owner" + accessor[1] + ";\n")
763 fn_defn = ptr_fn_defn
764 write_c("static inline " + fn_defn + "{\n")
765 if check_sfx is not None:
766 write_c("CHECK(" + check_sfx[0] + "owner" + check_sfx[1] + ");\n")
767 write_c("\treturn &" + accessor[0] + "owner" + accessor[1] + ";\n")
770 dummy_line = fn_defn + ";\n"
771 map_fn_with_ref_option(dummy_line, reg_fn_regex.match(dummy_line), None, None, "", holds_ref)
773 def map_tuple(struct_name, field_lines):
774 human_ty = struct_name.replace("LDKC2Tuple", "TwoTuple").replace("LDKC3Tuple", "ThreeTuple")
775 with open(f"{sys.argv[3]}/structs/{human_ty}{consts.file_ext}", "w") as out_java_struct:
776 out_java_struct.write(consts.map_tuple(struct_name))
778 for idx, (line, _) in enumerate(field_lines):
779 if idx != 0 and idx < len(field_lines) - 2:
780 ty_list.append(java_c_types(line.strip(';'), None))
781 tuple_types[struct_name] = (ty_list, struct_name)
783 # Map virtual getter functions
784 for idx, (line, _) in enumerate(field_lines):
785 if idx != 0 and idx < len(field_lines) - 2:
786 field_name = chr(ord('a') + idx - 1)
787 assert line.endswith(" " + field_name + ";")
788 create_getter(struct_name, line[:-3].strip(), field_name, ("", "->" + field_name), None)
790 out_java.write(consts.bindings_header)
791 with open(f"{sys.argv[2]}/version{consts.file_ext}", "w") as out_java_version:
792 out_java_version.write(consts.bindings_version_file.replace('<git_version_ldk_garbagecollected>', local_git_version))
794 with open(f"{sys.argv[3]}/structs/CommonBase{consts.file_ext}", "w") as out_java_struct:
795 out_java_struct.write(consts.common_base)
798 last_block_comment = None
801 const_val_regex = re.compile("^extern const ([A-Za-z_0-9]*) ([A-Za-z_0-9]*);$")
803 line_indicates_result_regex = re.compile("^ union (LDKCResult_[A-Za-z_0-9]*Ptr) contents;$")
804 line_indicates_vec_regex = re.compile("^ (struct |enum |union )?([A-Za-z_0-9]*) \*data;$")
805 line_indicates_opaque_regex = re.compile("^ bool is_owned;$")
806 line_indicates_trait_regex = re.compile("^ (struct |enum |union )?([A-Za-z_0-9]* \*?)\(\*([A-Za-z_0-9]*)\)\((const )?void \*this_arg(.*)\);$")
807 assert(line_indicates_trait_regex.match(" uintptr_t (*send_data)(void *this_arg, LDKu8slice data, bool resume_read);"))
808 assert(line_indicates_trait_regex.match(" struct LDKCVec_MessageSendEventZ (*get_and_clear_pending_msg_events)(const void *this_arg);"))
809 assert(line_indicates_trait_regex.match(" struct LDKCVec_u8Z (*write)(const void *this_arg);"))
810 line_indicates_trait_clone_regex = re.compile("^ void \(\*cloned\)\(struct ([A-Za-z0-9])* \*NONNULL_PTR new_[A-Za-z0-9]*\);$")
811 assert(line_indicates_trait_clone_regex.match(" void (*cloned)(struct LDKSign *NONNULL_PTR new_Sign);"))
812 line_field_var_regex = re.compile("^ struct ([A-Za-z_0-9]*) ([A-Za-z_0-9]*);$")
813 assert(line_field_var_regex.match(" struct LDKMessageSendEventsProvider MessageSendEventsProvider;"))
814 assert(line_field_var_regex.match(" struct LDKChannelPublicKeys pubkeys;"))
815 struct_name_regex = re.compile("^typedef (struct|enum|union) (MUST_USE_STRUCT )?(LDK[A-Za-z_0-9]*) {$")
816 assert(struct_name_regex.match("typedef struct LDKCVec_u8Z {"))
817 assert(struct_name_regex.match("typedef enum LDKNetwork {"))
819 union_enum_items = {}
820 result_ptr_struct_items = {}
822 if block_comment is not None:
823 if line.endswith("*/\n"):
824 last_block_comment = block_comment.strip("\n")
827 block_comment = block_comment + line.strip(" /*")
828 elif cur_block_obj is not None:
829 cur_block_obj = cur_block_obj + line
830 if line.startswith("} "):
834 obj_lines = cur_block_obj.split("\n")
836 result_contents = None
837 is_unitary_enum = False
838 is_union_enum = False
843 last_struct_block_comment = None
845 for idx, struct_line in enumerate(obj_lines):
846 if struct_line.strip().startswith("/*"):
847 block_comment = struct_line.strip(" /*")
848 if block_comment is not None:
849 if struct_line.endswith("*/"):
850 last_struct_block_comment = block_comment.strip("\n")
853 block_comment = block_comment + "\n" + struct_line.strip(" /*").replace("…", "...")
855 struct_name_match = struct_name_regex.match(struct_line)
856 if struct_name_match is not None:
857 struct_name = struct_name_match.group(3)
858 if struct_name_match.group(1) == "enum":
859 if not struct_name.endswith("_Tag"):
860 is_unitary_enum = True
863 elif struct_name_match.group(1) == "union":
865 if line_indicates_opaque_regex.match(struct_line):
867 result_match = line_indicates_result_regex.match(struct_line)
868 if result_match is not None:
869 result_contents = result_match.group(1)
870 vec_ty_match = line_indicates_vec_regex.match(struct_line)
871 if vec_ty_match is not None and struct_name.startswith("LDKCVec_"):
872 vec_ty = vec_ty_match.group(2)
873 elif struct_name.startswith("LDKC2Tuple_") or struct_name.startswith("LDKC3Tuple_"):
875 trait_fn_match = line_indicates_trait_regex.match(struct_line)
876 if trait_fn_match is not None:
877 trait_fn_lines.append((last_struct_block_comment, trait_fn_match))
878 trait_clone_fn_match = line_indicates_trait_clone_regex.match(struct_line)
879 if trait_clone_fn_match is not None:
880 trait_fn_lines.append((last_struct_block_comment, "cloned"))
881 field_var_match = line_field_var_regex.match(struct_line)
882 if field_var_match is not None:
883 field_var_lines.append(field_var_match)
884 field_lines.append((struct_line, last_struct_block_comment))
885 last_struct_block_comment = None
887 assert(struct_name is not None)
888 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))
889 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))
890 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))
891 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))
892 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))
893 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))
894 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))
897 opaque_structs.add(struct_name)
898 with open(f"{sys.argv[3]}/structs/{struct_name.replace('LDK', '')}{consts.file_ext}", "w") as out_java_struct:
899 out_opaque_struct_human = consts.map_opaque_struct(struct_name, last_block_comment)
900 last_block_comment = None
901 out_java_struct.write(out_opaque_struct_human)
902 elif result_contents is not None:
903 assert result_contents in result_ptr_struct_items
904 res_ty, err_ty = result_ptr_struct_items[result_contents]
905 map_result(struct_name, res_ty, err_ty)
906 elif struct_name.startswith("LDKCResult_") and struct_name.endswith("ZPtr"):
907 for line, _ in field_lines:
908 if line.endswith("*result;"):
909 res_ty = line[:-8].strip()
910 elif line.endswith("*err;"):
911 err_ty = line[:-5].strip()
912 result_ptr_struct_items[struct_name] = (res_ty, err_ty)
913 result_types.add(struct_name[:-3])
915 map_tuple(struct_name, field_lines)
916 elif vec_ty is not None:
917 ty_info = type_mapping_generator.map_type(vec_ty + " arr_elem", False, None, False, False)
918 if ty_info.is_native_primitive:
919 clone_fns.add(struct_name.replace("LDK", "") + "_clone")
920 write_c("static inline " + struct_name + " " + struct_name.replace("LDK", "") + "_clone(const " + struct_name + " *orig) {\n")
921 write_c("\t" + struct_name + " ret = { .data = MALLOC(sizeof(" + ty_info.c_ty + ") * orig->datalen, \"" + struct_name + " clone bytes\"), .datalen = orig->datalen };\n")
922 write_c("\tmemcpy(ret.data, orig->data, sizeof(" + ty_info.c_ty + ") * ret.datalen);\n")
923 write_c("\treturn ret;\n}\n")
924 elif (ty_info.rust_obj.replace("LDK", "") + "_clone") in clone_fns:
925 ty_name = struct_name.replace("LDK", "")
926 clone_fns.add(ty_name + "_clone")
927 write_c("static inline " + struct_name + " " + ty_name + "_clone(const " + struct_name + " *orig) {\n")
928 write_c("\t" + struct_name + " ret = { .data = MALLOC(sizeof(" + ty_info.rust_obj + ") * orig->datalen, \"" + struct_name + " clone bytes\"), .datalen = orig->datalen };\n")
929 write_c("\tfor (size_t i = 0; i < ret.datalen; i++) {\n")
930 write_c("\t\tret.data[i] = " + ty_info.rust_obj.replace("LDK", "") + "_clone(&orig->data[i]);\n")
931 write_c("\t}\n\treturn ret;\n}\n")
933 assert(struct_name.endswith("_Tag"))
934 struct_name = struct_name[:-4]
935 union_enum_items[struct_name] = {"field_lines": field_lines}
936 elif struct_name.endswith("_Body") and struct_name.split("_")[0] in union_enum_items:
937 enum_var_name = struct_name.split("_")
938 union_enum_items[enum_var_name[0]][enum_var_name[1]] = field_lines
939 elif struct_name in union_enum_items:
942 for line, _ in field_lines:
943 if line == " struct {":
947 elif elem_items > -1:
949 if line.startswith("struct "):
951 elif line.startswith("enum "):
953 split = line.split(" ")
954 assert len(split) == 2
955 tuple_variants[split[1].strip(";")] = split[0]
958 # We don't currently support tuple variant with more than one element
960 map_complex_enum(struct_name, union_enum_items[struct_name], tuple_variants, last_block_comment)
961 last_block_comment = None
962 elif is_unitary_enum:
963 map_unitary_enum(struct_name, field_lines, last_block_comment)
964 last_block_comment = None
965 elif len(trait_fn_lines) > 0:
966 map_trait(struct_name, field_var_lines, trait_fn_lines, last_block_comment)
967 elif struct_name == "LDKTxOut":
968 with open(f"{sys.argv[3]}/structs/TxOut{consts.file_ext}", "w") as out_java_struct:
969 out_java_struct.write(consts.hu_struct_file_prefix)
970 out_java_struct.write(consts.txout_defn)
971 fn_line = "struct LDKCVec_u8Z TxOut_get_script_pubkey (struct LDKTxOut* thing)"
972 write_c(fn_line + " {")
973 write_c("\treturn CVec_u8Z_clone(&thing->script_pubkey);")
975 map_fn(fn_line + "\n", re.compile("(.*) (TxOut_get_script_pubkey) \((.*)\)").match(fn_line), None, None, None)
976 fn_line = "uint64_t TxOut_get_value (struct LDKTxOut* thing)"
977 write_c(fn_line + " {")
978 write_c("\treturn thing->value;")
980 map_fn(fn_line + "\n", re.compile("(.*) (TxOut_get_value) \((.*)\)").match(fn_line), None, None, None)
982 pass # Everything remaining is a byte[] or some form
985 fn_ptr = fn_ptr_regex.match(line)
986 fn_ret_arr = fn_ret_arr_regex.match(line)
987 reg_fn = reg_fn_regex.match(line)
988 const_val = const_val_regex.match(line)
990 if line.startswith("#include <"):
992 elif line.startswith("/*"):
993 if not line.endswith("*/\n"):
994 block_comment = line.strip(" /*")
995 elif line.startswith("typedef enum "):
997 elif line.startswith("typedef struct "):
999 elif line.startswith("typedef union "):
1000 cur_block_obj = line
1001 elif fn_ptr is not None:
1002 map_fn(line, fn_ptr, None, None, last_block_comment)
1003 last_block_comment = None
1004 elif fn_ret_arr is not None:
1005 map_fn(line, fn_ret_arr, fn_ret_arr.group(4), None, last_block_comment)
1006 last_block_comment = None
1007 elif reg_fn is not None:
1008 map_fn(line, reg_fn, None, None, last_block_comment)
1009 last_block_comment = None
1010 elif const_val_regex is not None:
1011 # TODO Map const variables
1014 assert(line == "\n")
1016 out_java.write(consts.bindings_footer)
1017 for struct_name in opaque_structs:
1018 with open(f"{sys.argv[3]}/structs/{struct_name.replace('LDK', '')}{consts.file_ext}", "a") as out_java_struct:
1019 out_java_struct.write("}\n")
1020 for struct_name in trait_structs:
1021 with open(f"{sys.argv[3]}/structs/{struct_name.replace('LDK', '')}{consts.file_ext}", "a") as out_java_struct:
1022 out_java_struct.write("}\n")
1023 for struct_name in complex_enums:
1024 with open(f"{sys.argv[3]}/structs/{struct_name.replace('LDK', '').replace('COption', 'Option')}{consts.file_ext}", "a") as out_java_struct:
1025 out_java_struct.write("}\n")
1026 for struct_name in result_types:
1027 with open(f"{sys.argv[3]}/structs/{struct_name.replace('LDKCResult', 'Result')}{consts.file_ext}", "a") as out_java_struct:
1028 out_java_struct.write("}\n")
1029 for struct_name in tuple_types:
1030 struct_hu_name = struct_name.replace("LDKC2Tuple", "TwoTuple").replace("LDKC3Tuple", "ThreeTuple")
1031 with open(f"{sys.argv[3]}/structs/{struct_hu_name}{consts.file_ext}", "a") as out_java_struct:
1032 out_java_struct.write("}\n")
1034 with open(f"{sys.argv[4]}/bindings.c.body", "w") as out_c:
1035 out_c.write(consts.c_file_pfx)
1036 out_c.write(consts.init_str())
1038 with open(f"{sys.argv[4]}/version.c", "w") as out_c:
1039 out_c.write(consts.c_version_file.replace('<git_version_ldk_garbagecollected>', local_git_version))
1040 with open(f"{sys.argv[3]}/structs/UtilMethods{consts.file_ext}", "a") as util:
1041 util.write(consts.util_fn_sfx)