1
0

hgt.py 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  1. import os
  2. import sys
  3. import re
  4. import warnings
  5. from typing import List, Tuple, Dict
  6. FileList = List[str]
  7. FlatList = Dict[str, Tuple[str]]
  8. class RunError(Exception):
  9. def __init__(self, message="RunError: run as module is not allowed."):
  10. self.message = message
  11. class ArgError(Exception):
  12. def __init__(self, message="ArgError: just 6 arg is allowed."):
  13. self.message = message
  14. if __name__ != '__main__':
  15. raise RunError()
  16. argv = sys.argv
  17. if len(argv) < 7:
  18. warnings.warn(f"Too few argument [{len(argv)}]", UserWarning)
  19. raise ArgError()
  20. output_dir = argv[1]
  21. export_h = argv[2]
  22. export = argv[3]
  23. translation = argv[4]
  24. base_name = argv[5]
  25. input_dir = argv[6:]
  26. translation_output = os.path.join(output_dir, "tr")
  27. base_tr_file = os.path.join(translation, "base.py")
  28. try:
  29. os.makedirs(output_dir) # 生成输出目录
  30. except OSError:
  31. ...
  32. try:
  33. os.makedirs(translation_output) # 生成tr目录
  34. except OSError:
  35. ...
  36. # 执行base.py代码, 并保存结果
  37. base_tr = {}
  38. if os.path.exists(base_tr_file):
  39. with open(base_tr_file, "r", encoding="utf-8") as f:
  40. code = f.read()
  41. exec(code, base_tr, base_tr)
  42. def checkFileType(dest: str, src: List[str]) -> bool:
  43. for i in src:
  44. if i == dest:
  45. return True
  46. return False
  47. def getFileFromPath(paths: List[str], file_type_: List[str]) -> FileList:
  48. """
  49. 函数名: 获取目录列表中所有指定后缀的文件
  50. :param paths: 目录列表
  51. :param file_type_: 指定后在
  52. :return: 文件列表
  53. """
  54. tmp: FileList = []
  55. for path in paths:
  56. for file_path, dir_names, file_names in os.walk(path):
  57. for names in file_names:
  58. file_type = os.path.splitext(names)[-1]
  59. if checkFileType(file_type, file_type_):
  60. tmp.append(os.path.join(file_path, names))
  61. return tmp
  62. # 获取宏 HT_aFunGetText的列表并计算
  63. file_list: FileList = getFileFromPath(input_dir, ['.c', '.cpp', '.h', '.hpp'])
  64. pattern = re.compile(f'HT_{base_name}' + r'\(([\S]+),[\s]*(\"[^\"]*\")\)') # 宏的定义
  65. flat_list: FlatList = {}
  66. for file in file_list:
  67. with open(file, "r", encoding="utf-8") as f:
  68. data = f.readline()
  69. while data:
  70. result: List[str] = pattern.findall(data)
  71. for i in result:
  72. tmp = i[1]
  73. if tmp == "\"<base-tr>\"":
  74. tmp = f"\"{base_tr.get(i[0], None)}\""
  75. default_ = tmp.replace('\n', '\\n').replace('\r', '')
  76. if i[0] in flat_list:
  77. if flat_list[i[0]][0] != default_ and default_ != '""' and flat_list[i[0]][0] != '""':
  78. # 若果是空字串则可以覆盖
  79. warnings.warn(f"Double define text: {i[0]}", UserWarning)
  80. continue
  81. elif default_ == '""':
  82. default_ = flat_list[i[0]][0]
  83. flat_list[i[0]] = (default_,) # 生成一个数组
  84. data = f.readline()
  85. # 生成对应文件
  86. with open(os.path.join(output_dir, f"{base_name}_ht.py"), "w", encoding="utf-8") as fpy:
  87. with open(os.path.join(output_dir, f"{base_name}_ht.c"), "w", encoding="utf-8") as fc:
  88. with open(os.path.join(output_dir, f"{base_name}_ht.h.tmp"), "w", encoding="utf-8") as fh:
  89. head = f'''/*
  90. * File: {base_name}_ht.c/{base_name}_ht.c
  91. * The file is automatically generated by Huan-GetText
  92. * Encoding: utf-8
  93. */'''
  94. fc.write(head + '\n\n')
  95. fc.write(f"#include \"{base_name}_ht.h\"\n")
  96. fc.write(f"#undef HT_{base_name}\n")
  97. fh.write(head + '\n\n')
  98. fh.write("#ifndef HT_GETTEXT_H\n")
  99. fh.write("#define HT_GETTEXT_H\n")
  100. fh.write("#ifdef __cplusplus\n")
  101. fh.write('extern "C" {\n')
  102. fh.write("#endif\n")
  103. fh.write(f"#ifdef HT_{base_name}\n")
  104. fh.write(f"#error \"Double define HT_{base_name}\"\n")
  105. fh.write("#endif\n")
  106. fh.write(f"#include \"{export_h}.h\"\n")
  107. fh.write(f"#define HT_{base_name}(name, ...) ((char *)(HT_TEXT_{base_name}_ ## name))\n")
  108. fh.write(f"{export} int HT_init{base_name}GetText(char *lang);\n")
  109. fpy.write("# Example for tr file" + '\n\n')
  110. for i in flat_list:
  111. fc.write(f"{export} const char *HT_TEXT_{base_name}_{i} = {flat_list[i][0]};\n")
  112. fh.write(f"{export} extern const char *HT_TEXT_{base_name}_{i};\n")
  113. fpy.write(f"# {i}: str = \"\" # {flat_list[i][0]}\n")
  114. fc.write(f'''\n
  115. /* define init{base_name}GetText */
  116. /* need dlfcn or dlfcn-win32 */
  117. #include "dlfcn.h"
  118. #include "stdlib.h"
  119. static void *handle = NULL;
  120. int HT_init{base_name}GetText(char *lang) {{
  121. if (lang == NULL || handle != NULL)
  122. return 2;
  123. handle = dlopen(lang, RTLD_NOW);
  124. if (handle == NULL)
  125. return 1;
  126. char **tmp;\n\n''')
  127. for i in flat_list:
  128. fc.write(f''' tmp = dlsym(handle, "HT__TEXT_{base_name}_{i}");\n''')
  129. fc.write(f''' if (tmp != NULL) HT_TEXT_{base_name}_{i} = *tmp;\n\n''')
  130. fc.write(' return 0;\n}\n')
  131. fh.write("#ifdef __cplusplus\n")
  132. fh.write('}\n')
  133. fh.write("#endif\n")
  134. fh.write("#endif\n")
  135. import hashlib
  136. with open(os.path.join(output_dir, f"{base_name}_ht.h.tmp"), "r", encoding="utf-8") as fht:
  137. fht_data = fht.read()
  138. fht_md5 = hashlib.md5(fht.read().encode('utf-8')).hexdigest()
  139. try:
  140. with open(os.path.join(output_dir, f"{base_name}_ht.h"), "r", encoding="utf-8") as fh:
  141. fh_md5 = hashlib.md5(fh.read().encode('utf-8')).hexdigest()
  142. if fh_md5 != fht_md5:
  143. raise Exception
  144. except:
  145. with open(os.path.join(output_dir, f"{base_name}_ht.h"), "w", encoding="utf-8") as fh:
  146. fh.write(fht.read())
  147. print(f"Write file {base_name}_ht.h")
  148. else:
  149. print(f"File {base_name}_ht.h already update")
  150. for i in flat_list:
  151. print(f"TEXT: {i}")
  152. translation_list: FileList = getFileFromPath([translation], ['.py'])
  153. for t in translation_list:
  154. name = os.path.splitext(os.path.split(t)[-1])[0]
  155. if name == 'base':
  156. continue
  157. print(f"tr: {name}")
  158. with open(t, "r", encoding="utf-8") as f:
  159. code = f.read()
  160. var = {}
  161. for i in flat_list:
  162. var[i] = flat_list[i][0][1:-1] # [1:-1] 去除引号
  163. exec(code, var, var)
  164. with open(os.path.join(translation_output, f"{name}.c"), "w", encoding="utf-8") as fc:
  165. fc.write(f"#include \"{export_h}.h\"\n") # 需要导出
  166. for i in flat_list: # 根据 flat_list 生成变量
  167. var[i].strip() # 去除首尾不需要的符号
  168. res = var[i].replace('\n', '\\n').replace('\r', '')
  169. fc.write(f"{export} const char *const HT__TEXT_{base_name}_{i} = \"{res}\";\n")