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 *"):
288 java_ty = consts.java_type_map["String"]
289 java_hu_ty = consts.java_hu_type_map["String"]
291 fn_ty_arg = "Ljava/lang/String;"
292 fn_arg = fn_arg[6:].strip()
293 elif fn_arg.startswith("LDKStr"):
295 java_ty = consts.java_type_map["String"]
296 java_hu_ty = consts.java_hu_type_map["String"]
298 fn_ty_arg = "Ljava/lang/String;"
299 fn_arg = fn_arg[6:].strip()
303 ma = var_ty_regex.match(fn_arg)
304 if ma.group(1).strip() in unitary_enums:
305 assert ma.group(1).strip().startswith("LDK")
306 java_ty = ma.group(1).strip()[3:]
308 c_ty = consts.result_c_ty
309 fn_ty_arg = "Lorg/ldk/enums/" + java_ty + ";"
310 fn_arg = ma.group(2).strip()
311 rust_obj = ma.group(1).strip()
313 c_ty = consts.ptr_c_ty
314 java_ty = consts.ptr_native_ty
315 java_hu_ty = ma.group(1).strip()
316 java_hu_ty = java_hu_ty.replace("LDKCOption", "Option")
317 java_hu_ty = java_hu_ty.replace("LDKCResult", "Result")
318 java_hu_ty = java_hu_ty.replace("LDKC2Tuple", "TwoTuple")
319 java_hu_ty = java_hu_ty.replace("LDKC3Tuple", "ThreeTuple")
320 java_hu_ty = java_hu_ty.replace("LDK", "")
322 fn_arg = ma.group(2).strip()
323 rust_obj = ma.group(1).strip()
324 if rust_obj in trait_structs:
325 contains_trait = True
326 elif rust_obj in complex_enums:
327 contains_trait = complex_enums[rust_obj]
330 if fn_arg.startswith(" *") or fn_arg.startswith("*"):
331 fn_arg = fn_arg.replace("*", "").strip()
333 c_ty = consts.ptr_c_ty
334 java_ty = consts.ptr_native_ty
337 var_is_arr = var_is_arr_regex.match(fn_arg)
338 if var_is_arr is not None or ret_arr_len is not None:
339 assert(not take_by_ptr)
341 # is there a special case for plurals?
342 if len(mapped_type) == 2:
343 java_ty = mapped_type[1]
345 java_ty = java_ty + "[]"
346 c_ty = c_ty + "Array"
347 if var_is_arr is not None:
348 if var_is_arr.group(1) == "":
349 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,
350 passed_as_ptr=False, is_ptr=False, nonnull_ptr=nonnull_ptr, var_name="arg",
351 arr_len=var_is_arr.group(2), arr_access=arr_access, is_native_primitive=False, contains_trait=contains_trait)
352 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,
353 passed_as_ptr=False, is_ptr=False, nonnull_ptr=nonnull_ptr, var_name=var_is_arr.group(1),
354 arr_len=var_is_arr.group(2), arr_access=arr_access, is_native_primitive=False, contains_trait=contains_trait)
356 if java_hu_ty is None:
358 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,
359 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,
360 contains_trait=contains_trait)
362 fn_ptr_regex = re.compile("^extern const ([A-Za-z_0-9\* ]*) \(\*(.*)\)\((.*)\);$")
363 fn_ret_arr_regex = re.compile("(.*) \(\*(.*)\((.*)\)\)\[([0-9]*)\];$")
364 reg_fn_regex = re.compile("([A-Za-z_0-9\* ]* \*?)([a-zA-Z_0-9]*)\((.*)\);$")
368 from gen_type_mapping import TypeMappingGenerator
369 type_mapping_generator = TypeMappingGenerator(java_c_types, consts, opaque_structs, clone_fns, unitary_enums, trait_structs, complex_enums, result_types, tuple_types)
371 with open(sys.argv[1]) as in_h:
373 reg_fn = reg_fn_regex.match(line)
374 if reg_fn is not None:
375 if reg_fn.group(2).endswith("_clone"):
376 clone_fns.add(reg_fn.group(2))
378 rty = java_c_types(reg_fn.group(1), None)
379 if rty is not None and not rty.is_native_primitive and reg_fn.group(2) == rty.java_hu_ty + "_new":
380 constructor_fns[rty.rust_obj] = reg_fn.group(3)
382 arr_fn = fn_ret_arr_regex.match(line)
383 if arr_fn is not None:
384 if arr_fn.group(2).endswith("_clone"):
385 clone_fns.add(arr_fn.group(2))
386 # No object constructors return arrays, as then they wouldn't be an object constructor
389 # Define some manual clones...
390 clone_fns.add("ThirtyTwoBytes_clone")
391 write_c("static inline struct LDKThirtyTwoBytes ThirtyTwoBytes_clone(const struct LDKThirtyTwoBytes *orig) { struct LDKThirtyTwoBytes ret; memcpy(ret.data, orig->data, 32); return ret; }\n")
393 java_c_types_none_allowed = False # C structs created by cbindgen are declared in dependency order
395 with open(f"{sys.argv[3]}/structs/UtilMethods{consts.file_ext}", "a") as util:
396 util.write(consts.util_fn_pfx)
398 with open(sys.argv[1]) as in_h, open(f"{sys.argv[2]}/bindings{consts.file_ext}", "w") as out_java:
399 # Map a top-level function
400 def map_fn(line, re_match, ret_arr_len, c_call_string, doc_comment):
401 map_fn_with_ref_option(line, re_match, ret_arr_len, c_call_string, doc_comment, False)
402 def map_fn_with_ref_option(line, re_match, ret_arr_len, c_call_string, doc_comment, force_holds_ref):
403 method_return_type = re_match.group(1)
404 method_name = re_match.group(2)
405 method_comma_separated_arguments = re_match.group(3)
406 method_arguments = method_comma_separated_arguments.split(',')
408 if method_name.startswith("__"):
411 is_free = method_name.endswith("_free")
412 if method_name.startswith("COption") or method_name.startswith("CResult"):
413 struct_meth = method_name.rsplit("Z", 1)[0][1:] + "Z"
414 expected_struct = "LDKC" + struct_meth
415 struct_meth_name = method_name[len(struct_meth) + 1:].strip("_")
416 elif method_name.startswith("C2Tuple") or method_name.startswith("C3Tuple"):
417 tuple_name = method_name.rsplit("Z", 1)[0][2:] + "Z"
418 if method_name.startswith("C2Tuple"):
419 struct_meth = "Two" + tuple_name
420 expected_struct = "LDKC2" + tuple_name
422 struct_meth = "Three" + tuple_name
423 expected_struct = "LDKC3" + tuple_name
424 struct_meth_name = method_name[len(tuple_name) + 2:].strip("_")
426 struct_meth = method_name.split("_")[0]
427 expected_struct = "LDK" + struct_meth
428 struct_meth_name = method_name[len(struct_meth) + 1 if len(struct_meth) != 0 else 0:].strip("_")
430 (params_nullable, ret_nullable) = doc_to_params_ret_nullable(doc_comment)
432 return_type_info = type_mapping_generator.map_nullable_type(method_return_type.strip() + " ret", True, ret_arr_len, False, force_holds_ref)
434 return_type_info = type_mapping_generator.map_type(method_return_type.strip() + " ret", True, ret_arr_len, False, force_holds_ref)
436 if method_name.endswith("_clone") and expected_struct not in unitary_enums:
437 meth_line = "uintptr_t " + expected_struct.replace("LDK", "") + "_clone_ptr(" + expected_struct + " *NONNULL_PTR arg)"
438 write_c("static inline " + meth_line + " {\n")
439 write_c("\t" + return_type_info.ret_conv[0].replace("\n", "\n\t"))
440 write_c(method_name + "(arg)")
441 write_c(return_type_info.ret_conv[1])
442 write_c("\n\treturn " + return_type_info.ret_conv_name + ";\n}\n")
443 map_fn(meth_line + ";\n", re.compile("(uintptr_t) ([A-Za-z_0-9]*)\((.*)\)").match(meth_line), None, None, None)
446 default_constructor_args = {}
448 takes_self_ptr = False
451 for argument_index, argument in enumerate(method_arguments):
452 arg_ty = type_mapping_generator.java_c_types(argument, None)
453 argument_conversion_info = None
454 if argument_index == 0 and arg_ty.java_hu_ty == struct_meth:
455 argument_conversion_info = type_mapping_generator.map_type_with_info(arg_ty, False, None, is_free, True, False)
457 if argument_conversion_info.ty_info.is_ptr:
458 takes_self_ptr = True
459 elif arg_ty.var_name in params_nullable:
460 argument_conversion_info = type_mapping_generator.map_type_with_info(arg_ty, False, None, is_free, True, True)
461 if argument_conversion_info.arg_conv is not None and "Warning" in argument_conversion_info.arg_conv:
462 arg_ty_info = java_c_types(argument, None)
463 print("WARNING: Remapping argument " + arg_ty_info.var_name + " of function " + method_name + " to a reference")
464 print(" The argument appears to require a move, or not clonable, and is nullable.")
465 print(" Normally for arguments that require a move and are not clonable, we split")
466 print(" the argument into the type's constructor's arguments and just use those to")
467 print(" construct a new object on the fly.")
468 print(" However, because the object is nullable, doing so would mean we can no")
469 print(" longer allow the user to pass null, as we now have an argument list instead.")
470 print(" Thus, we blindly assume its really an Option<&Type> instead of an Option<Type>.")
471 print(" It may or may not actually be a reference, but its the simplest mapping option")
472 print(" and also the only use of this code today.")
473 arg_ty_info.requires_clone = False
474 argument_conversion_info = type_mapping_generator.map_type_with_info(arg_ty_info, False, None, is_free, True, True)
475 assert argument_conversion_info.nullable
476 assert argument_conversion_info.arg_conv is not None and "Warning" not in argument_conversion_info.arg_conv
478 argument_conversion_info = type_mapping_generator.map_type_with_info(arg_ty, False, None, is_free, True, False)
480 if argument_conversion_info.arg_conv is not None and "Warning" in argument_conversion_info.arg_conv:
481 if argument_conversion_info.rust_obj in constructor_fns:
483 for explode_arg in constructor_fns[argument_conversion_info.rust_obj].split(','):
484 explode_arg_conv = type_mapping_generator.map_type(explode_arg, False, None, False, True)
485 if explode_arg_conv.c_ty == "void":
486 # We actually want to handle this case, but for now its only used in NetGraphMsgHandler::new()
487 # which ends up resulting in a redundant constructor - both without arguments for the NetworkGraph.
490 if not argument_conversion_info.arg_name in default_constructor_args:
491 default_constructor_args[argument_conversion_info.arg_name] = []
492 default_constructor_args[argument_conversion_info.arg_name].append(explode_arg_conv)
493 argument_types.append(argument_conversion_info)
495 if not takes_self and return_type_info.java_hu_ty != struct_meth:
496 if not return_type_info.java_hu_ty.startswith("Result_" + struct_meth):
497 struct_meth_name = method_name
501 impl_on_struct = (expected_struct in opaque_structs or expected_struct in trait_structs or
502 expected_struct in complex_enums or expected_struct in complex_enums or
503 expected_struct in result_types or expected_struct in tuple_types) and not is_free
504 impl_on_utils = not impl_on_struct and (not is_free and not method_name.endswith("_clone") and
505 not method_name.startswith("TxOut") and
506 not method_name.startswith("_") and
507 method_name != "check_platform" and method_name != "Result_read" and
508 not expected_struct in unitary_enums and
509 ((not method_name.startswith("C2Tuple_") and not method_name.startswith("C3Tuple_"))
510 or method_name.endswith("_read")))
512 # If we're adding a static method, and it returns a primitive or an array of primitives,
513 # and a variable conversion adds a reference on the return type (via `this`), skip the
514 # variable's conversion reference-add (as we obviously cannot need a reference).
515 if impl_on_utils and (return_type_info.is_native_primitive or
516 (return_type_info.ty_info.subty is not None and return_type_info.ty_info.subty.is_native_primitive)):
517 for arg in argument_types:
518 if arg.from_hu_conv is not None and arg.from_hu_conv[1] != "":
519 if "this" in arg.from_hu_conv[1]:
520 arg.from_hu_conv = (arg.from_hu_conv[0], "")
522 out_java.write("\t// " + line)
523 (out_java_delta, out_c_delta, out_java_struct_delta) = \
524 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)
525 out_java.write(out_java_delta)
528 assert len(argument_types) == 1
529 assert return_type_info.c_ty == "void"
530 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")
531 if argument_types[0].ty_info.passed_as_ptr and not argument_types[0].ty_info.rust_obj in opaque_structs:
532 write_c("\tif ((" + argument_types[0].ty_info.var_name + " & 1) != 0) return;\n")
534 for info in argument_types:
535 if info.arg_conv is not None:
536 write_c("\t" + info.arg_conv.replace('\n', "\n\t") + "\n")
537 assert c_call_string is None
538 write_c("\t" + method_name + "(")
539 if argument_types[0].arg_conv_name is not None:
540 write_c(argument_types[0].arg_conv_name)
542 for info in argument_types:
543 if info.arg_conv_cleanup is not None:
544 write_c("\n\t" + info.arg_conv_cleanup.replace("\n", "\n\t"))
549 out_java_struct = None
551 out_java_struct = open(f"{sys.argv[3]}/structs/{struct_meth}{consts.file_ext}", "a")
552 out_java_struct.write(out_java_struct_delta)
554 out_java_struct = open(f"{sys.argv[3]}/structs/UtilMethods{consts.file_ext}", "a")
555 for line in out_java_struct_delta.splitlines():
556 out_java_struct.write(line + "\n")
558 def map_unitary_enum(struct_name, field_lines, enum_doc_comment):
559 assert struct_name.startswith("LDK")
560 with open(f"{sys.argv[3]}/enums/{struct_name[3:]}{consts.file_ext}", "w") as out_java_enum:
561 unitary_enums.add(struct_name)
562 for idx, (struct_line, _) in enumerate(field_lines):
564 assert(struct_line == "typedef enum %s {" % struct_name)
565 elif idx == len(field_lines) - 3:
566 assert(struct_line.endswith("_Sentinel,"))
567 elif idx == len(field_lines) - 2:
568 assert(struct_line == "} %s;" % struct_name)
569 elif idx == len(field_lines) - 1:
570 assert(struct_line == "")
571 assert struct_name.startswith("LDK")
572 (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)
574 out_java_enum.write(native_file_out)
575 out_java.write(native_out)
577 def map_complex_enum(struct_name, union_enum_items, inline_enum_variants, enum_doc_comment):
578 java_hu_type = struct_name.replace("LDK", "").replace("COption", "Option")
581 tag_field_lines = union_enum_items["field_lines"]
582 contains_trait = False
583 for idx, (struct_line, variant_docs) in enumerate(tag_field_lines):
585 assert(struct_line == "typedef enum %s_Tag {" % struct_name)
586 elif idx == len(tag_field_lines) - 3:
587 assert(struct_line.endswith("_Sentinel,"))
588 elif idx == len(tag_field_lines) - 2:
589 assert(struct_line == "} %s_Tag;" % struct_name)
590 elif idx == len(tag_field_lines) - 1:
591 assert(struct_line == "")
593 variant_name = struct_line.strip(' ,')[len(struct_name) + 1:]
595 if "LDK" + variant_name in union_enum_items:
596 enum_var_lines = union_enum_items["LDK" + variant_name]
597 for idx, (field, field_docs) in enumerate(enum_var_lines):
598 if idx != 0 and idx < len(enum_var_lines) - 2 and field.strip() != "":
599 field_ty = type_mapping_generator.java_c_types(field.strip(' ;'), None)
600 contains_trait |= field_ty.contains_trait
601 if field_docs is not None and doc_to_field_nullable(field_docs):
602 field_conv = type_mapping_generator.map_type_with_info(field_ty, False, None, False, True, True)
604 field_conv = type_mapping_generator.map_type_with_info(field_ty, False, None, False, True, False)
605 fields.append((field_conv, field_docs))
606 enum_variants.append(ComplexEnumVariantInfo(variant_name, variant_docs, fields, False))
607 elif camel_to_snake(variant_name) in inline_enum_variants:
608 # TODO: If we ever have a rust enum Variant(Option<Struct>) we need to pipe
609 # docs through to there, and then potentially mark the field nullable.
610 mapped = type_mapping_generator.map_type(inline_enum_variants[camel_to_snake(variant_name)] + " " + camel_to_snake(variant_name), False, None, False, True)
611 contains_trait |= mapped.ty_info.contains_trait
612 fields.append((mapped, None))
613 enum_variants.append(ComplexEnumVariantInfo(variant_name, variant_docs, fields, True))
615 enum_variants.append(ComplexEnumVariantInfo(variant_name, variant_docs, fields, True))
616 complex_enums[struct_name] = contains_trait
618 with open(f"{sys.argv[3]}/structs/{java_hu_type}{consts.file_ext}", "w") as out_java_enum:
619 (out_java_addendum, out_java_enum_addendum, out_c_addendum) = consts.map_complex_enum(struct_name, enum_variants, camel_to_snake, enum_doc_comment)
621 out_java_enum.write(out_java_enum_addendum)
622 out_java.write(out_java_addendum)
623 write_c(out_c_addendum)
625 def map_trait(struct_name, field_var_lines, trait_fn_lines, trait_doc_comment):
626 with open(f"{sys.argv[3]}/structs/{struct_name.replace('LDK', '')}{consts.file_ext}", "w") as out_java_trait:
628 flattened_field_var_convs = []
629 for var_line in field_var_lines:
630 if var_line.group(1) in trait_structs:
631 field_var_convs.append((var_line.group(1), var_line.group(2), trait_structs[var_line.group(1)]))
632 flattened_field_var_convs.append((var_line.group(1), var_line.group(2), ))
633 flattened_field_var_convs.extend(trait_structs[var_line.group(1)])
635 mapped = type_mapping_generator.map_type(var_line.group(1) + " " + var_line.group(2), False, None, False, False)
636 field_var_convs.append(mapped)
637 flattened_field_var_convs.append(mapped)
638 trait_structs[struct_name] = field_var_convs
641 for fn_docs, fn_line in trait_fn_lines:
642 if fn_line == "cloned":
643 ret_ty_info = type_mapping_generator.map_type("void", True, None, False, False)
644 field_fns.append(TraitMethInfo("cloned", False, ret_ty_info, [], fn_docs))
646 (nullable_params, ret_nullable) = doc_to_params_ret_nullable(fn_docs)
648 assert False # This isn't yet handled on the Java side
649 ret_ty_info.nullable = True
650 ret_ty_info = type_mapping_generator.map_nullable_type(fn_line.group(2).strip() + " ret", True, None, False, False)
652 ret_ty_info = type_mapping_generator.map_type(fn_line.group(2).strip() + " ret", True, None, False, False)
653 is_const = fn_line.group(4) is not None
656 for idx, arg in enumerate(fn_line.group(5).split(',')):
659 arg_ty_info = type_mapping_generator.java_c_types(arg, None)
660 if arg_ty_info.var_name in nullable_params:
661 # Types that are actually null instead of all-0s aren't yet handled on the Java side:
662 arg_conv_info = type_mapping_generator.map_type_with_info(arg_ty_info, True, None, False, False, True)
664 arg_conv_info = type_mapping_generator.map_type_with_info(arg_ty_info, True, None, False, False, False)
665 arg_tys.append(arg_conv_info)
666 field_fns.append(TraitMethInfo(fn_line.group(3), is_const, ret_ty_info, arg_tys, fn_docs))
668 (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)
669 write_c(out_c_addendum)
670 out_java_trait.write(out_java_trait_addendum)
671 out_java.write(out_java_addendum)
673 for fn_docs, fn_line in trait_fn_lines:
674 if fn_line == "cloned":
676 # For now, just disable enabling the _call_log - we don't know how to inverse-map String
677 is_log = fn_line.group(3) == "log" and struct_name == "LDKLogger"
678 if fn_line.group(3) != "free" and fn_line.group(3) != "eq" and not is_log:
679 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"
680 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)
681 for idx, var_line in enumerate(field_var_lines):
682 if var_line.group(1) not in trait_structs:
683 write_c(var_line.group(1) + " " + struct_name + "_set_get_" + var_line.group(2) + "(" + struct_name + "* this_arg) {\n")
684 write_c("\tif (this_arg->set_" + var_line.group(2) + " != NULL)\n")
685 write_c("\t\tthis_arg->set_" + var_line.group(2) + "(this_arg);\n")
686 write_c("\treturn this_arg->" + var_line.group(2) + ";\n")
688 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"
689 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)
691 def map_result(struct_name, res_ty, err_ty):
692 result_types.add(struct_name)
693 human_ty = struct_name.replace("LDKCResult", "Result")
694 res_map = type_mapping_generator.map_type(res_ty + " res", True, None, False, True)
695 err_map = type_mapping_generator.map_type(err_ty + " err", True, None, False, True)
696 java_hu_struct = consts.map_result(struct_name, res_map, err_map)
697 create_getter(struct_name, res_ty, "ok", ("*", "->contents.result"), ("", "->result_ok"))
698 create_getter(struct_name, err_ty, "err", ("*", "->contents.err"), ("!", "->result_ok"))
699 with open(f"{sys.argv[3]}/structs/{human_ty}{consts.file_ext}", "w") as out_java_struct:
700 out_java_struct.write(java_hu_struct)
702 def create_getter(struct_name, field_decl, field_name, accessor, check_sfx):
703 field_ty = java_c_types(field_decl + " " + field_name, None)
704 ptr_fn_defn = field_decl + " *" + struct_name.replace("LDK", "") + "_get_" + field_name + "(" + struct_name + " *NONNULL_PTR owner)"
705 owned_fn_defn = field_decl + " " + struct_name.replace("LDK", "") + "_get_" + field_name + "(" + struct_name + " *NONNULL_PTR owner)"
708 if field_ty.rust_obj is not None and field_ty.rust_obj.replace("LDK", "") + "_clone" in clone_fns:
709 fn_defn = owned_fn_defn
710 write_c("static inline " + fn_defn + "{\n")
711 if check_sfx is not None:
712 write_c("CHECK(" + check_sfx[0] + "owner" + check_sfx[1] + ");\n")
713 write_c("\treturn " + field_ty.rust_obj.replace("LDK", "") + "_clone(&" + accessor[0] + "owner" + accessor[1] + ");\n")
714 elif field_ty.arr_len is not None or field_ty.is_native_primitive or field_ty.rust_obj in unitary_enums:
715 fn_defn = owned_fn_defn
716 write_c("static inline " + fn_defn + "{\n")
717 if check_sfx is not None:
718 write_c("CHECK(" + check_sfx[0] + "owner" + check_sfx[1] + ");\n")
719 write_c("\treturn " + accessor[0] + "owner" + accessor[1] + ";\n")
722 fn_defn = ptr_fn_defn
723 write_c("static inline " + fn_defn + "{\n")
724 if check_sfx is not None:
725 write_c("CHECK(" + check_sfx[0] + "owner" + check_sfx[1] + ");\n")
726 write_c("\treturn &" + accessor[0] + "owner" + accessor[1] + ";\n")
729 dummy_line = fn_defn + ";\n"
730 map_fn_with_ref_option(dummy_line, reg_fn_regex.match(dummy_line), None, None, "", holds_ref)
732 def map_tuple(struct_name, field_lines):
733 human_ty = struct_name.replace("LDKC2Tuple", "TwoTuple").replace("LDKC3Tuple", "ThreeTuple")
734 with open(f"{sys.argv[3]}/structs/{human_ty}{consts.file_ext}", "w") as out_java_struct:
735 out_java_struct.write(consts.map_tuple(struct_name))
737 for idx, (line, _) in enumerate(field_lines):
738 if idx != 0 and idx < len(field_lines) - 2:
739 ty_list.append(java_c_types(line.strip(';'), None))
740 tuple_types[struct_name] = (ty_list, struct_name)
742 # Map virtual getter functions
743 for idx, (line, _) in enumerate(field_lines):
744 if idx != 0 and idx < len(field_lines) - 2:
745 field_name = chr(ord('a') + idx - 1)
746 assert line.endswith(" " + field_name + ";")
747 create_getter(struct_name, line[:-3].strip(), field_name, ("", "->" + field_name), None)
749 out_java.write(consts.bindings_header)
750 with open(f"{sys.argv[2]}/version{consts.file_ext}", "w") as out_java_version:
751 out_java_version.write(consts.bindings_version_file.replace('<git_version_ldk_garbagecollected>', local_git_version))
753 with open(f"{sys.argv[3]}/structs/CommonBase{consts.file_ext}", "w") as out_java_struct:
754 out_java_struct.write(consts.common_base)
757 last_block_comment = None
760 const_val_regex = re.compile("^extern const ([A-Za-z_0-9]*) ([A-Za-z_0-9]*);$")
762 line_indicates_result_regex = re.compile("^ union (LDKCResult_[A-Za-z_0-9]*Ptr) contents;$")
763 line_indicates_vec_regex = re.compile("^ (struct |enum |union )?([A-Za-z_0-9]*) \*data;$")
764 line_indicates_opaque_regex = re.compile("^ bool is_owned;$")
765 line_indicates_trait_regex = re.compile("^ (struct |enum |union )?([A-Za-z_0-9]* \*?)\(\*([A-Za-z_0-9]*)\)\((const )?void \*this_arg(.*)\);$")
766 assert(line_indicates_trait_regex.match(" uintptr_t (*send_data)(void *this_arg, LDKu8slice data, bool resume_read);"))
767 assert(line_indicates_trait_regex.match(" struct LDKCVec_MessageSendEventZ (*get_and_clear_pending_msg_events)(const void *this_arg);"))
768 assert(line_indicates_trait_regex.match(" struct LDKCVec_u8Z (*write)(const void *this_arg);"))
769 line_indicates_trait_clone_regex = re.compile("^ void \(\*cloned\)\(struct ([A-Za-z0-9])* \*NONNULL_PTR new_[A-Za-z0-9]*\);$")
770 assert(line_indicates_trait_clone_regex.match(" void (*cloned)(struct LDKSign *NONNULL_PTR new_Sign);"))
771 line_field_var_regex = re.compile("^ struct ([A-Za-z_0-9]*) ([A-Za-z_0-9]*);$")
772 assert(line_field_var_regex.match(" struct LDKMessageSendEventsProvider MessageSendEventsProvider;"))
773 assert(line_field_var_regex.match(" struct LDKChannelPublicKeys pubkeys;"))
774 struct_name_regex = re.compile("^typedef (struct|enum|union) (MUST_USE_STRUCT )?(LDK[A-Za-z_0-9]*) {$")
775 assert(struct_name_regex.match("typedef struct LDKCVec_u8Z {"))
776 assert(struct_name_regex.match("typedef enum LDKNetwork {"))
778 union_enum_items = {}
779 result_ptr_struct_items = {}
781 if block_comment is not None:
782 if line.endswith("*/\n"):
783 last_block_comment = block_comment.strip("\n")
786 block_comment = block_comment + line.strip(" /*")
787 elif cur_block_obj is not None:
788 cur_block_obj = cur_block_obj + line
789 if line.startswith("} "):
793 obj_lines = cur_block_obj.split("\n")
795 result_contents = None
796 is_unitary_enum = False
797 is_union_enum = False
802 last_struct_block_comment = None
804 for idx, struct_line in enumerate(obj_lines):
805 if struct_line.strip().startswith("/*"):
806 block_comment = struct_line.strip(" /*")
807 if block_comment is not None:
808 if struct_line.endswith("*/"):
809 last_struct_block_comment = block_comment.strip("\n")
812 block_comment = block_comment + "\n" + struct_line.strip(" /*").replace("…", "...")
814 struct_name_match = struct_name_regex.match(struct_line)
815 if struct_name_match is not None:
816 struct_name = struct_name_match.group(3)
817 if struct_name_match.group(1) == "enum":
818 if not struct_name.endswith("_Tag"):
819 is_unitary_enum = True
822 elif struct_name_match.group(1) == "union":
824 if line_indicates_opaque_regex.match(struct_line):
826 result_match = line_indicates_result_regex.match(struct_line)
827 if result_match is not None:
828 result_contents = result_match.group(1)
829 vec_ty_match = line_indicates_vec_regex.match(struct_line)
830 if vec_ty_match is not None and struct_name.startswith("LDKCVec_"):
831 vec_ty = vec_ty_match.group(2)
832 elif struct_name.startswith("LDKC2Tuple_") or struct_name.startswith("LDKC3Tuple_"):
834 trait_fn_match = line_indicates_trait_regex.match(struct_line)
835 if trait_fn_match is not None:
836 trait_fn_lines.append((last_struct_block_comment, trait_fn_match))
837 trait_clone_fn_match = line_indicates_trait_clone_regex.match(struct_line)
838 if trait_clone_fn_match is not None:
839 trait_fn_lines.append((last_struct_block_comment, "cloned"))
840 field_var_match = line_field_var_regex.match(struct_line)
841 if field_var_match is not None:
842 field_var_lines.append(field_var_match)
843 field_lines.append((struct_line, last_struct_block_comment))
844 last_struct_block_comment = None
846 assert(struct_name is not None)
847 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))
848 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))
849 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))
850 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))
851 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))
852 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))
853 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))
856 opaque_structs.add(struct_name)
857 with open(f"{sys.argv[3]}/structs/{struct_name.replace('LDK', '')}{consts.file_ext}", "w") as out_java_struct:
858 out_opaque_struct_human = consts.map_opaque_struct(struct_name, last_block_comment)
859 last_block_comment = None
860 out_java_struct.write(out_opaque_struct_human)
861 elif result_contents is not None:
862 assert result_contents in result_ptr_struct_items
863 res_ty, err_ty = result_ptr_struct_items[result_contents]
864 map_result(struct_name, res_ty, err_ty)
865 elif struct_name.startswith("LDKCResult_") and struct_name.endswith("ZPtr"):
866 for line, _ in field_lines:
867 if line.endswith("*result;"):
868 res_ty = line[:-8].strip()
869 elif line.endswith("*err;"):
870 err_ty = line[:-5].strip()
871 result_ptr_struct_items[struct_name] = (res_ty, err_ty)
872 result_types.add(struct_name[:-3])
874 map_tuple(struct_name, field_lines)
875 elif vec_ty is not None:
876 ty_info = type_mapping_generator.map_type(vec_ty + " arr_elem", False, None, False, False)
877 if ty_info.is_native_primitive:
878 clone_fns.add(struct_name.replace("LDK", "") + "_clone")
879 write_c("static inline " + struct_name + " " + struct_name.replace("LDK", "") + "_clone(const " + struct_name + " *orig) {\n")
880 write_c("\t" + struct_name + " ret = { .data = MALLOC(sizeof(" + ty_info.c_ty + ") * orig->datalen, \"" + struct_name + " clone bytes\"), .datalen = orig->datalen };\n")
881 write_c("\tmemcpy(ret.data, orig->data, sizeof(" + ty_info.c_ty + ") * ret.datalen);\n")
882 write_c("\treturn ret;\n}\n")
883 elif (ty_info.rust_obj.replace("LDK", "") + "_clone") in clone_fns:
884 ty_name = "CVec_" + ty_info.rust_obj.replace("LDK", "") + "Z";
885 clone_fns.add(ty_name + "_clone")
886 write_c("static inline " + struct_name + " " + ty_name + "_clone(const " + struct_name + " *orig) {\n")
887 write_c("\t" + struct_name + " ret = { .data = MALLOC(sizeof(" + ty_info.rust_obj + ") * orig->datalen, \"" + struct_name + " clone bytes\"), .datalen = orig->datalen };\n")
888 write_c("\tfor (size_t i = 0; i < ret.datalen; i++) {\n")
889 write_c("\t\tret.data[i] = " + ty_info.rust_obj.replace("LDK", "") + "_clone(&orig->data[i]);\n")
890 write_c("\t}\n\treturn ret;\n}\n")
892 assert(struct_name.endswith("_Tag"))
893 struct_name = struct_name[:-4]
894 union_enum_items[struct_name] = {"field_lines": field_lines}
895 elif struct_name.endswith("_Body") and struct_name.split("_")[0] in union_enum_items:
896 enum_var_name = struct_name.split("_")
897 union_enum_items[enum_var_name[0]][enum_var_name[1]] = field_lines
898 elif struct_name in union_enum_items:
901 for line, _ in field_lines:
902 if line == " struct {":
906 elif elem_items > -1:
908 if line.startswith("struct "):
910 elif line.startswith("enum "):
912 split = line.split(" ")
913 assert len(split) == 2
914 tuple_variants[split[1].strip(";")] = split[0]
917 # We don't currently support tuple variant with more than one element
919 map_complex_enum(struct_name, union_enum_items[struct_name], tuple_variants, last_block_comment)
920 last_block_comment = None
921 elif is_unitary_enum:
922 map_unitary_enum(struct_name, field_lines, last_block_comment)
923 last_block_comment = None
924 elif len(trait_fn_lines) > 0:
925 map_trait(struct_name, field_var_lines, trait_fn_lines, last_block_comment)
926 elif struct_name == "LDKTxOut":
927 with open(f"{sys.argv[3]}/structs/TxOut{consts.file_ext}", "w") as out_java_struct:
928 out_java_struct.write(consts.hu_struct_file_prefix)
929 out_java_struct.write(consts.txout_defn)
930 fn_line = "struct LDKCVec_u8Z TxOut_get_script_pubkey (struct LDKTxOut* thing)"
931 write_c(fn_line + " {")
932 write_c("\treturn CVec_u8Z_clone(&thing->script_pubkey);")
934 map_fn(fn_line + "\n", re.compile("(.*) (TxOut_get_script_pubkey) \((.*)\)").match(fn_line), None, None, None)
935 fn_line = "uint64_t TxOut_get_value (struct LDKTxOut* thing)"
936 write_c(fn_line + " {")
937 write_c("\treturn thing->value;")
939 map_fn(fn_line + "\n", re.compile("(.*) (TxOut_get_value) \((.*)\)").match(fn_line), None, None, None)
941 pass # Everything remaining is a byte[] or some form
944 fn_ptr = fn_ptr_regex.match(line)
945 fn_ret_arr = fn_ret_arr_regex.match(line)
946 reg_fn = reg_fn_regex.match(line)
947 const_val = const_val_regex.match(line)
949 if line.startswith("#include <"):
951 elif line.startswith("/*"):
952 if not line.endswith("*/\n"):
953 block_comment = line.strip(" /*")
954 elif line.startswith("typedef enum "):
956 elif line.startswith("typedef struct "):
958 elif line.startswith("typedef union "):
960 elif fn_ptr is not None:
961 map_fn(line, fn_ptr, None, None, last_block_comment)
962 last_block_comment = None
963 elif fn_ret_arr is not None:
964 map_fn(line, fn_ret_arr, fn_ret_arr.group(4), None, last_block_comment)
965 last_block_comment = None
966 elif reg_fn is not None:
967 map_fn(line, reg_fn, None, None, last_block_comment)
968 last_block_comment = None
969 elif const_val_regex is not None:
970 # TODO Map const variables
975 out_java.write(consts.bindings_footer)
976 for struct_name in opaque_structs:
977 with open(f"{sys.argv[3]}/structs/{struct_name.replace('LDK', '')}{consts.file_ext}", "a") as out_java_struct:
978 out_java_struct.write("}\n")
979 for struct_name in trait_structs:
980 with open(f"{sys.argv[3]}/structs/{struct_name.replace('LDK', '')}{consts.file_ext}", "a") as out_java_struct:
981 out_java_struct.write("}\n")
982 for struct_name in complex_enums:
983 with open(f"{sys.argv[3]}/structs/{struct_name.replace('LDK', '').replace('COption', 'Option')}{consts.file_ext}", "a") as out_java_struct:
984 out_java_struct.write("}\n")
985 for struct_name in result_types:
986 with open(f"{sys.argv[3]}/structs/{struct_name.replace('LDKCResult', 'Result')}{consts.file_ext}", "a") as out_java_struct:
987 out_java_struct.write("}\n")
988 for struct_name in tuple_types:
989 struct_hu_name = struct_name.replace("LDKC2Tuple", "TwoTuple").replace("LDKC3Tuple", "ThreeTuple")
990 with open(f"{sys.argv[3]}/structs/{struct_hu_name}{consts.file_ext}", "a") as out_java_struct:
991 out_java_struct.write("}\n")
993 with open(f"{sys.argv[4]}/bindings.c.body", "w") as out_c:
994 out_c.write(consts.c_file_pfx)
995 out_c.write(consts.init_str())
997 with open(f"{sys.argv[4]}/version.c", "w") as out_c:
998 out_c.write(consts.c_version_file.replace('<git_version_ldk_garbagecollected>', local_git_version))
999 with open(f"{sys.argv[3]}/structs/UtilMethods{consts.file_ext}", "a") as util:
1000 util.write(consts.util_fn_sfx)