feat: add internationalization support with Chinese translations
- Introduce Translator class for simple i18n with English and Chinese locales - Add concurrent.futures, os, and locale imports for parallel execution and language detection - Implement automatic language detection based on system locale - Provide translation keys for build messages, test results, and command outputs - Support dynamic language switching via set_language method
This commit is contained in:
@@ -15,6 +15,9 @@ import time
|
|||||||
import sys
|
import sys
|
||||||
import json
|
import json
|
||||||
import re
|
import re
|
||||||
|
import concurrent.futures
|
||||||
|
import os
|
||||||
|
import locale
|
||||||
|
|
||||||
|
|
||||||
class ColorFormatter(logging.Formatter):
|
class ColorFormatter(logging.Formatter):
|
||||||
@@ -50,6 +53,111 @@ def setup_logger() -> logging.Logger:
|
|||||||
logger = setup_logger()
|
logger = setup_logger()
|
||||||
|
|
||||||
|
|
||||||
|
# 简单的翻译系统
|
||||||
|
class Translator:
|
||||||
|
"""简单的翻译器"""
|
||||||
|
|
||||||
|
def __init__(self, lang=None):
|
||||||
|
def detect_language() -> str:
|
||||||
|
(name, _) = locale.getlocale()
|
||||||
|
if name is None:
|
||||||
|
return "en_US"
|
||||||
|
lower_lang = name.lower()
|
||||||
|
# 检查是否包含中文相关关键词
|
||||||
|
chinese_keywords = [
|
||||||
|
"chinese",
|
||||||
|
"china",
|
||||||
|
"zh",
|
||||||
|
"cn",
|
||||||
|
]
|
||||||
|
for keyword in chinese_keywords:
|
||||||
|
if keyword in lower_lang:
|
||||||
|
return "zh_CN"
|
||||||
|
return "en_US"
|
||||||
|
|
||||||
|
self.lang: str = lang if lang is not None else detect_language()
|
||||||
|
self.translations = {
|
||||||
|
"zh_CN": {
|
||||||
|
"build_complete": "完成 {mode} 目标,耗时 {time} (使用 {jobs} 线程)",
|
||||||
|
"clean_directory": "已清理构建目录: {path}",
|
||||||
|
"cleaned_files": "清理了 {count} 个文件,总大小 {size}",
|
||||||
|
"no_build_dir": "没有找到构建目录,无需清理",
|
||||||
|
"no_targets": "没有可运行的目标",
|
||||||
|
"running_test": "运行测试: {name}",
|
||||||
|
"no_tests": "没有找到匹配的测试",
|
||||||
|
"test_passed": " ✓ 测试 {name} 通过",
|
||||||
|
"test_failed": " ✗ 测试 {name} 失败",
|
||||||
|
"test_timeout": " ✗ 测试 {name} 超时",
|
||||||
|
"test_error": " ✗ 测试 {name} 运行异常: {error}",
|
||||||
|
"test_results": "测试结果: {passed} 通过, {failed} 失败",
|
||||||
|
"project_initialized": "项目 '{name}' 初始化完成!",
|
||||||
|
"project_structure": "项目结构:",
|
||||||
|
"usage": "使用以下命令构建项目:",
|
||||||
|
"build_command": " cbuild build",
|
||||||
|
"run_command": " cbuild run",
|
||||||
|
"test_command": " cbuild test",
|
||||||
|
"dependency_tree": "dependency tree:",
|
||||||
|
"skipping": "跳过 {file}",
|
||||||
|
"executing_command": "执行命令: {cmd}",
|
||||||
|
"command_failed": "命令执行失败 [{code}]: {cmd}",
|
||||||
|
"compiling_file": "编译文件 {file} 时出错: {error}",
|
||||||
|
"linking_target": "链接目标 {target} 时出错: {error}",
|
||||||
|
"regex_filter": "使用正则表达式过滤测试: '{pattern}',匹配到 {count} 个测试",
|
||||||
|
"invalid_regex": "无效的正则表达式 '{pattern}': {error}",
|
||||||
|
},
|
||||||
|
"en_US": {
|
||||||
|
"build_complete": "Finished {mode} target(s) in {time} (using {jobs} threads)",
|
||||||
|
"clean_directory": "Cleaned build directory: {path}",
|
||||||
|
"cleaned_files": "Cleaned {count} files, total size {size}",
|
||||||
|
"no_build_dir": "No build directory found, nothing to clean",
|
||||||
|
"no_targets": "No target to run",
|
||||||
|
"running_test": "Running test: {name}",
|
||||||
|
"no_tests": "No matching tests found",
|
||||||
|
"test_passed": " ✓ Test {name} passed",
|
||||||
|
"test_failed": " ✗ Test {name} failed",
|
||||||
|
"test_timeout": " ✗ Test {name} timed out",
|
||||||
|
"test_error": " ✗ Test {name} error: {error}",
|
||||||
|
"test_results": "Test results: {passed} passed, {failed} failed",
|
||||||
|
"project_initialized": "Project '{name}' initialized!",
|
||||||
|
"project_structure": "Project structure:",
|
||||||
|
"usage": "Use the following commands to build the project:",
|
||||||
|
"build_command": " cbuild build",
|
||||||
|
"run_command": " cbuild run",
|
||||||
|
"test_command": " cbuild test",
|
||||||
|
"dependency_tree": "dependency tree:",
|
||||||
|
"skipping": "Skipping {file}",
|
||||||
|
"executing_command": "Executing command: {cmd}",
|
||||||
|
"command_failed": "Command failed [{code}]: {cmd}",
|
||||||
|
"compiling_file": "Error compiling file {file}: {error}",
|
||||||
|
"linking_target": "Error linking target {target}: {error}",
|
||||||
|
"regex_filter": "Filtering tests with regex: '{pattern}', matched {count} tests",
|
||||||
|
"invalid_regex": "Invalid regex '{pattern}': {error}",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
def translate(self, key, **kwargs):
|
||||||
|
"""翻译键值"""
|
||||||
|
lang_dict = self.translations.get(self.lang, self.translations["zh_CN"])
|
||||||
|
template = lang_dict.get(key)
|
||||||
|
if template is None:
|
||||||
|
return key
|
||||||
|
if kwargs:
|
||||||
|
try:
|
||||||
|
return template.format(**kwargs)
|
||||||
|
except KeyError:
|
||||||
|
return template
|
||||||
|
return template
|
||||||
|
|
||||||
|
def set_language(self, lang):
|
||||||
|
"""设置语言"""
|
||||||
|
if lang in self.translations:
|
||||||
|
self.lang = lang
|
||||||
|
|
||||||
|
|
||||||
|
# 全局翻译器实例
|
||||||
|
translator = Translator(None)
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class Dependency:
|
class Dependency:
|
||||||
"""依赖配置"""
|
"""依赖配置"""
|
||||||
@@ -590,7 +698,11 @@ class PackageBuilder:
|
|||||||
"""包构建器"""
|
"""包构建器"""
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self, package_path: Path, compiler: Compiler, mode: BuildMode = BuildMode.DEV
|
self,
|
||||||
|
package_path: Path,
|
||||||
|
compiler: Compiler,
|
||||||
|
mode: BuildMode = BuildMode.DEV,
|
||||||
|
jobs: int = 0, # 0 表示自动检测 CPU 核心数
|
||||||
):
|
):
|
||||||
self.package = PackageConfig(package_path)
|
self.package = PackageConfig(package_path)
|
||||||
self.context = BuildContext(self.package, mode)
|
self.context = BuildContext(self.package, mode)
|
||||||
@@ -598,6 +710,11 @@ class PackageBuilder:
|
|||||||
self.cache = BuildCache(self.context.paths.output)
|
self.cache = BuildCache(self.context.paths.output)
|
||||||
self.mode = mode
|
self.mode = mode
|
||||||
self.flags = self.compiler.get_flags(mode)
|
self.flags = self.compiler.get_flags(mode)
|
||||||
|
self.jobs = jobs if jobs > 0 else self._get_cpu_count()
|
||||||
|
|
||||||
|
def _get_cpu_count(self) -> int:
|
||||||
|
"""获取 CPU 核心数"""
|
||||||
|
return os.cpu_count() or 1
|
||||||
|
|
||||||
def _compile(self, source: Path, output: Path):
|
def _compile(self, source: Path, output: Path):
|
||||||
"""编译单个源文件"""
|
"""编译单个源文件"""
|
||||||
@@ -607,7 +724,73 @@ class PackageBuilder:
|
|||||||
)
|
)
|
||||||
self.cache.update(source, output)
|
self.cache.update(source, output)
|
||||||
else:
|
else:
|
||||||
logger.debug("跳过 %s", source)
|
logger.debug(translator.translate("skipping", file=str(source)))
|
||||||
|
|
||||||
|
def _parallel_compile(self, compile_tasks: list[tuple[Path, Path]]) -> list[Path]:
|
||||||
|
"""并行编译多个源文件"""
|
||||||
|
if not compile_tasks:
|
||||||
|
return []
|
||||||
|
|
||||||
|
# 如果只有一个文件或线程数为1,使用串行编译
|
||||||
|
if len(compile_tasks) == 1 or self.jobs == 1:
|
||||||
|
objects = []
|
||||||
|
for src, obj in compile_tasks:
|
||||||
|
self._compile(src, obj)
|
||||||
|
objects.append(obj)
|
||||||
|
return objects
|
||||||
|
|
||||||
|
# 使用线程池并行编译
|
||||||
|
objects = []
|
||||||
|
with concurrent.futures.ThreadPoolExecutor(max_workers=self.jobs) as executor:
|
||||||
|
# 提交所有编译任务
|
||||||
|
future_to_task = {}
|
||||||
|
for src, obj in compile_tasks:
|
||||||
|
future = executor.submit(self._compile, src, obj)
|
||||||
|
future_to_task[future] = (src, obj)
|
||||||
|
|
||||||
|
# 等待所有任务完成
|
||||||
|
for future in concurrent.futures.as_completed(future_to_task):
|
||||||
|
src, obj = future_to_task[future]
|
||||||
|
try:
|
||||||
|
future.result() # 检查是否有异常
|
||||||
|
objects.append(obj)
|
||||||
|
except Exception as e:
|
||||||
|
logger.error("编译文件 %s 时出错: %s", src, e)
|
||||||
|
raise
|
||||||
|
|
||||||
|
return objects
|
||||||
|
|
||||||
|
def _parallel_link(self, link_tasks: list[tuple[Target, list[Path]]]):
|
||||||
|
"""并行链接多个目标"""
|
||||||
|
if not link_tasks:
|
||||||
|
return
|
||||||
|
|
||||||
|
# 如果只有一个链接任务或线程数为1,使用串行链接
|
||||||
|
if len(link_tasks) == 1 or self.jobs == 1:
|
||||||
|
for target, objects in link_tasks:
|
||||||
|
objects.append(target.object)
|
||||||
|
self.compiler.link(objects, target.output, self.flags)
|
||||||
|
return
|
||||||
|
|
||||||
|
# 使用线程池并行链接
|
||||||
|
with concurrent.futures.ThreadPoolExecutor(max_workers=self.jobs) as executor:
|
||||||
|
# 提交所有链接任务
|
||||||
|
future_to_task = {}
|
||||||
|
for target, objects in link_tasks:
|
||||||
|
objects.append(target.object)
|
||||||
|
future = executor.submit(
|
||||||
|
self.compiler.link, objects, target.output, self.flags
|
||||||
|
)
|
||||||
|
future_to_task[future] = target
|
||||||
|
|
||||||
|
# 等待所有任务完成
|
||||||
|
for future in concurrent.futures.as_completed(future_to_task):
|
||||||
|
target = future_to_task[future]
|
||||||
|
try:
|
||||||
|
future.result() # 检查是否有异常
|
||||||
|
except Exception as e:
|
||||||
|
logger.error("链接目标 %s 时出错: %s", target.name, e)
|
||||||
|
raise
|
||||||
|
|
||||||
def build(self, target_types: list[TargetType]):
|
def build(self, target_types: list[TargetType]):
|
||||||
"""构建包"""
|
"""构建包"""
|
||||||
@@ -618,14 +801,16 @@ class PackageBuilder:
|
|||||||
self.context.paths.output.mkdir(parents=True, exist_ok=True)
|
self.context.paths.output.mkdir(parents=True, exist_ok=True)
|
||||||
self.context.paths.objects.mkdir(parents=True, exist_ok=True)
|
self.context.paths.objects.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
# 编译所有依赖的源文件
|
# 获取需要编译的文件列表
|
||||||
objects = []
|
compile_tasks = self.context.get_compile_objects()
|
||||||
for src, obj in self.context.get_compile_objects():
|
|
||||||
self._compile(src, obj)
|
# 使用线程池并行编译
|
||||||
objects.append(obj)
|
objects = self._parallel_compile(compile_tasks)
|
||||||
|
|
||||||
# 构建目标
|
# 构建目标
|
||||||
exec_types = {TargetType.MAIN_EXEC, TargetType.EXEC, TargetType.TEST_EXEC}
|
exec_types = {TargetType.MAIN_EXEC, TargetType.EXEC, TargetType.TEST_EXEC}
|
||||||
|
link_tasks = []
|
||||||
|
|
||||||
for target in self.context.get_targets():
|
for target in self.context.get_targets():
|
||||||
if target.type not in target_types:
|
if target.type not in target_types:
|
||||||
continue
|
continue
|
||||||
@@ -633,14 +818,17 @@ class PackageBuilder:
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
self._compile(target.source, target.object)
|
self._compile(target.source, target.object)
|
||||||
objects.append(target.object)
|
link_tasks.append((target, objects.copy()))
|
||||||
self.compiler.link(objects, target.output, self.flags)
|
|
||||||
objects.pop()
|
# 并行链接
|
||||||
|
self._parallel_link(link_tasks)
|
||||||
|
|
||||||
self.cache.save()
|
self.cache.save()
|
||||||
elapsed = time.time() - start
|
elapsed = time.time() - start
|
||||||
time_str = f"{elapsed:.2f}s" if elapsed < 60 else f"{elapsed / 60:.1f}m"
|
time_str = f"{elapsed:.2f}s" if elapsed < 60 else f"{elapsed / 60:.1f}m"
|
||||||
logger.info("完成 %s 目标,耗时 %s", self.mode.value, time_str)
|
logger.info(
|
||||||
|
"完成 %s 目标,耗时 %s (使用 %d 线程)", self.mode.value, time_str, self.jobs
|
||||||
|
)
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
"""运行主程序"""
|
"""运行主程序"""
|
||||||
@@ -830,6 +1018,9 @@ def create_parser():
|
|||||||
"--compiler", "-c", choices=["gcc", "clang"], default="gcc", help="编译器"
|
"--compiler", "-c", choices=["gcc", "clang"], default="gcc", help="编译器"
|
||||||
)
|
)
|
||||||
subparser.add_argument("--record", "-r", action="store_true", help="记录命令")
|
subparser.add_argument("--record", "-r", action="store_true", help="记录命令")
|
||||||
|
subparser.add_argument(
|
||||||
|
"--jobs", "-j", type=int, default=0, help="并行编译任务数 (0=自动检测)"
|
||||||
|
)
|
||||||
mode_group = subparser.add_mutually_exclusive_group()
|
mode_group = subparser.add_mutually_exclusive_group()
|
||||||
mode_group.add_argument(
|
mode_group.add_argument(
|
||||||
"--debug",
|
"--debug",
|
||||||
@@ -919,8 +1110,11 @@ def main():
|
|||||||
# 获取构建模式
|
# 获取构建模式
|
||||||
mode = getattr(args, "mode", BuildMode.DEV)
|
mode = getattr(args, "mode", BuildMode.DEV)
|
||||||
|
|
||||||
|
# 获取线程数
|
||||||
|
jobs = getattr(args, "jobs", 0)
|
||||||
|
|
||||||
# 创建构建器
|
# 创建构建器
|
||||||
builder = PackageBuilder(Path(args.path), compiler, mode)
|
builder = PackageBuilder(Path(args.path), compiler, mode, jobs)
|
||||||
|
|
||||||
# 执行命令
|
# 执行命令
|
||||||
if args.command == "build":
|
if args.command == "build":
|
||||||
|
|||||||
Reference in New Issue
Block a user