import os import sys import re import warnings from typing import List, Tuple, Dict FileList = List[str] FlatList = Dict[str, Tuple[str]] class RunError(Exception): def __init__(self, message="RunError: run as module is not allowed."): self.message = message class ArgError(Exception): def __init__(self, message="ArgError: just 6 arg is allowed."): self.message = message if __name__ != '__main__': raise RunError() argv = sys.argv if len(argv) < 7: warnings.warn(f"Too few argument [{len(argv)}]", UserWarning) raise ArgError() output_dir = argv[1] export_h = argv[2] export = argv[3] translation = argv[4] base_name = argv[5] input_dir = argv[6:] translation_output = os.path.join(output_dir, "tr") base_tr_file = os.path.join(translation, "base.py") try: os.makedirs(output_dir) # 生成输出目录 except OSError: ... try: os.makedirs(translation_output) # 生成tr目录 except OSError: ... # 执行base.py代码, 并保存结果 base_tr = {} if os.path.exists(base_tr_file): with open(base_tr_file, "r", encoding="utf-8") as f: code = f.read() exec(code, base_tr, base_tr) def checkFileType(dest: str, src: List[str]) -> bool: for i in src: if i == dest: return True return False def getFileFromPath(paths: List[str], file_type_: List[str]) -> FileList: """ 函数名: 获取目录列表中所有指定后缀的文件 :param paths: 目录列表 :param file_type_: 指定后在 :return: 文件列表 """ tmp: FileList = [] for path in paths: for file_path, dir_names, file_names in os.walk(path): for names in file_names: file_type = os.path.splitext(names)[-1] if checkFileType(file_type, file_type_): tmp.append(os.path.join(file_path, names)) return tmp # 获取宏 HT_aFunGetText的列表并计算 file_list: FileList = getFileFromPath(input_dir, ['.c', '.cpp', '.h', '.hpp']) pattern = re.compile(f'HT_{base_name}' + r'\(([\S]+),[\s]*(\"[^\"]*\")\)') # 宏的定义 flat_list: FlatList = {} for file in file_list: with open(file, "r", encoding="utf-8") as f: data = f.readline() while data: result: List[str] = pattern.findall(data) for i in result: tmp = i[1] if tmp == "\"\"": tmp = f"\"{base_tr.get(i[0], None)}\"" default_ = tmp.replace('\n', '\\n').replace('\r', '') if i[0] in flat_list: if flat_list[i[0]][0] != default_ and default_ != '""' and flat_list[i[0]][0] != '""': # 若果是空字串则可以覆盖 warnings.warn(f"Double define text: {i[0]}", UserWarning) continue elif default_ == '""': default_ = flat_list[i[0]][0] flat_list[i[0]] = (default_,) # 生成一个数组 data = f.readline() # 生成对应文件 with open(os.path.join(output_dir, f"{base_name}_ht.py"), "w", encoding="utf-8") as fpy: with open(os.path.join(output_dir, f"{base_name}_ht.c"), "w", encoding="utf-8") as fc: with open(os.path.join(output_dir, f"{base_name}_ht.h.tmp"), "w", encoding="utf-8") as fh: head = f'''/* * File: {base_name}_ht.c/{base_name}_ht.c * The file is automatically generated by Huan-GetText * Encoding: utf-8 */''' fc.write(head + '\n\n') fc.write(f"#include \"{base_name}_ht.h\"\n") fc.write(f"#undef HT_{base_name}\n") fh.write(head + '\n\n') fh.write("#ifndef HT_GETTEXT_H\n") fh.write("#define HT_GETTEXT_H\n") fh.write("#ifdef __cplusplus\n") fh.write('extern "C" {\n') fh.write("#endif\n") fh.write(f"#ifdef HT_{base_name}\n") fh.write(f"#error \"Double define HT_{base_name}\"\n") fh.write("#endif\n") fh.write(f"#include \"{export_h}.h\"\n") fh.write(f"#define HT_{base_name}(name, ...) ((char *)(HT_TEXT_{base_name}_ ## name))\n") fh.write(f"{export} int HT_init{base_name}GetText(char *lang);\n") fpy.write("# Example for tr file" + '\n\n') for i in flat_list: fc.write(f"{export} const char *HT_TEXT_{base_name}_{i} = {flat_list[i][0]};\n") fh.write(f"{export} extern const char *HT_TEXT_{base_name}_{i};\n") fpy.write(f"# {i}: str = \"\" # {flat_list[i][0]}\n") fc.write(f'''\n /* define init{base_name}GetText */ /* need dlfcn or dlfcn-win32 */ #include "dlfcn.h" #include "stdlib.h" static void *handle = NULL; int HT_init{base_name}GetText(char *lang) {{ if (lang == NULL || handle != NULL) return 2; handle = dlopen(lang, RTLD_NOW); if (handle == NULL) return 1; char **tmp;\n\n''') for i in flat_list: fc.write(f''' tmp = dlsym(handle, "HT__TEXT_{base_name}_{i}");\n''') fc.write(f''' if (tmp != NULL) HT_TEXT_{base_name}_{i} = *tmp;\n\n''') fc.write(' return 0;\n}\n') fh.write("#ifdef __cplusplus\n") fh.write('}\n') fh.write("#endif\n") fh.write("#endif\n") import hashlib with open(os.path.join(output_dir, f"{base_name}_ht.h.tmp"), "r", encoding="utf-8") as fht: fht_data = fht.read() fht_md5 = hashlib.md5(fht.read().encode('utf-8')).hexdigest() try: with open(os.path.join(output_dir, f"{base_name}_ht.h"), "r", encoding="utf-8") as fh: fh_md5 = hashlib.md5(fh.read().encode('utf-8')).hexdigest() if fh_md5 != fht_md5: raise Exception except: with open(os.path.join(output_dir, f"{base_name}_ht.h"), "w", encoding="utf-8") as fh: fh.write(fht.read()) print(f"Write file {base_name}_ht.h") else: print(f"File {base_name}_ht.h already update") for i in flat_list: print(f"TEXT: {i}") translation_list: FileList = getFileFromPath([translation], ['.py']) for t in translation_list: name = os.path.splitext(os.path.split(t)[-1])[0] if name == 'base': continue print(f"tr: {name}") with open(t, "r", encoding="utf-8") as f: code = f.read() var = {} for i in flat_list: var[i] = flat_list[i][0][1:-1] # [1:-1] 去除引号 exec(code, var, var) with open(os.path.join(translation_output, f"{name}.c"), "w", encoding="utf-8") as fc: fc.write(f"#include \"{export_h}.h\"\n") # 需要导出 for i in flat_list: # 根据 flat_list 生成变量 var[i].strip() # 去除首尾不需要的符号 res = var[i].replace('\n', '\\n').replace('\r', '') fc.write(f"{export} const char *const HT__TEXT_{base_name}_{i} = \"{res}\";\n")